j?j:g+f));return 1===d?(b=a[c-1],e.push(k[b>>2]+k[63&b<<4]+"==")):2===d&&(b=(a[c-2]<<8)+a[c-1],e.push(k[b>>10]+k[63&b>>4]+k[63&b<<2]+"=")),e.join("")}c.byteLength=function(a){var b=d(a),c=b[0],e=b[1];return 3*(c+e)/4-e},c.toByteArray=f,c.fromByteArray=j;for(var k=[],l=[],m="undefined"==typeof Uint8Array?Array:Uint8Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,p=n.length;o 0) {
+ throw new Error('Invalid string. Length must be a multiple of 4')
+ }
+
+ // Trim off extra bytes after placeholder bytes are found
+ // See: https://github.com/beatgammit/base64-js/issues/42
+ var validLen = b64.indexOf('=')
+ if (validLen === -1) validLen = len
+
+ var placeHoldersLen = validLen === len
+ ? 0
+ : 4 - (validLen % 4)
+
+ return [validLen, placeHoldersLen]
+}
+
+// base64 is 4/3 + up to two characters of the original data
+function byteLength (b64) {
+ var lens = getLens(b64)
+ var validLen = lens[0]
+ var placeHoldersLen = lens[1]
+ return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
+}
+
+function _byteLength (b64, validLen, placeHoldersLen) {
+ return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
+}
+
+function toByteArray (b64) {
+ var tmp
+ var lens = getLens(b64)
+ var validLen = lens[0]
+ var placeHoldersLen = lens[1]
+
+ var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
+
+ var curByte = 0
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ var len = placeHoldersLen > 0
+ ? validLen - 4
+ : validLen
+
+ var i
+ for (i = 0; i < len; i += 4) {
+ tmp =
+ (revLookup[b64.charCodeAt(i)] << 18) |
+ (revLookup[b64.charCodeAt(i + 1)] << 12) |
+ (revLookup[b64.charCodeAt(i + 2)] << 6) |
+ revLookup[b64.charCodeAt(i + 3)]
+ arr[curByte++] = (tmp >> 16) & 0xFF
+ arr[curByte++] = (tmp >> 8) & 0xFF
+ arr[curByte++] = tmp & 0xFF
+ }
+
+ if (placeHoldersLen === 2) {
+ tmp =
+ (revLookup[b64.charCodeAt(i)] << 2) |
+ (revLookup[b64.charCodeAt(i + 1)] >> 4)
+ arr[curByte++] = tmp & 0xFF
+ }
+
+ if (placeHoldersLen === 1) {
+ tmp =
+ (revLookup[b64.charCodeAt(i)] << 10) |
+ (revLookup[b64.charCodeAt(i + 1)] << 4) |
+ (revLookup[b64.charCodeAt(i + 2)] >> 2)
+ arr[curByte++] = (tmp >> 8) & 0xFF
+ arr[curByte++] = tmp & 0xFF
+ }
+
+ return arr
+}
+
+function tripletToBase64 (num) {
+ return lookup[num >> 18 & 0x3F] +
+ lookup[num >> 12 & 0x3F] +
+ lookup[num >> 6 & 0x3F] +
+ lookup[num & 0x3F]
+}
+
+function encodeChunk (uint8, start, end) {
+ var tmp
+ var output = []
+ for (var i = start; i < end; i += 3) {
+ tmp =
+ ((uint8[i] << 16) & 0xFF0000) +
+ ((uint8[i + 1] << 8) & 0xFF00) +
+ (uint8[i + 2] & 0xFF)
+ output.push(tripletToBase64(tmp))
+ }
+ return output.join('')
+}
+
+function fromByteArray (uint8) {
+ var tmp
+ var len = uint8.length
+ var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
+ var parts = []
+ var maxChunkLength = 16383 // must be multiple of 3
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
+ parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ if (extraBytes === 1) {
+ tmp = uint8[len - 1]
+ parts.push(
+ lookup[tmp >> 2] +
+ lookup[(tmp << 4) & 0x3F] +
+ '=='
+ )
+ } else if (extraBytes === 2) {
+ tmp = (uint8[len - 2] << 8) + uint8[len - 1]
+ parts.push(
+ lookup[tmp >> 10] +
+ lookup[(tmp >> 4) & 0x3F] +
+ lookup[(tmp << 2) & 0x3F] +
+ '='
+ )
+ }
+
+ return parts.join('')
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/base64-js/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/base64-js/package.json
new file mode 100644
index 000000000..c3972e39f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/base64-js/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "base64-js",
+ "description": "Base64 encoding/decoding in pure JS",
+ "version": "1.5.1",
+ "author": "T. Jameson Little ",
+ "typings": "index.d.ts",
+ "bugs": {
+ "url": "https://github.com/beatgammit/base64-js/issues"
+ },
+ "devDependencies": {
+ "babel-minify": "^0.5.1",
+ "benchmark": "^2.1.4",
+ "browserify": "^16.3.0",
+ "standard": "*",
+ "tape": "4.x"
+ },
+ "homepage": "https://github.com/beatgammit/base64-js",
+ "keywords": [
+ "base64"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/beatgammit/base64-js.git"
+ },
+ "scripts": {
+ "build": "browserify -s base64js -r ./ | minify > base64js.min.js",
+ "lint": "standard",
+ "test": "npm run lint && npm run unit",
+ "unit": "tape test/*.js"
+ },
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/LICENSE.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/LICENSE.md
new file mode 100644
index 000000000..5a92289f6
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/LICENSE.md
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/README.md
new file mode 100644
index 000000000..5b3e7a81b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/README.md
@@ -0,0 +1,98 @@
+node-bindings
+=============
+### Helper module for loading your native module's `.node` file
+
+This is a helper module for authors of Node.js native addon modules.
+It is basically the "swiss army knife" of `require()`ing your native module's
+`.node` file.
+
+Throughout the course of Node's native addon history, addons have ended up being
+compiled in a variety of different places, depending on which build tool and which
+version of node was used. To make matters worse, now the `gyp` build tool can
+produce either a __Release__ or __Debug__ build, each being built into different
+locations.
+
+This module checks _all_ the possible locations that a native addon would be built
+at, and returns the first one that loads successfully.
+
+
+Installation
+------------
+
+Install with `npm`:
+
+``` bash
+$ npm install --save bindings
+```
+
+Or add it to the `"dependencies"` section of your `package.json` file.
+
+
+Example
+-------
+
+`require()`ing the proper bindings file for the current node version, platform
+and architecture is as simple as:
+
+``` js
+var bindings = require('bindings')('binding.node')
+
+// Use your bindings defined in your C files
+bindings.your_c_function()
+```
+
+
+Nice Error Output
+-----------------
+
+When the `.node` file could not be loaded, `node-bindings` throws an Error with
+a nice error message telling you exactly what was tried. You can also check the
+`err.tries` Array property.
+
+```
+Error: Could not load the bindings file. Tried:
+ → /Users/nrajlich/ref/build/binding.node
+ → /Users/nrajlich/ref/build/Debug/binding.node
+ → /Users/nrajlich/ref/build/Release/binding.node
+ → /Users/nrajlich/ref/out/Debug/binding.node
+ → /Users/nrajlich/ref/Debug/binding.node
+ → /Users/nrajlich/ref/out/Release/binding.node
+ → /Users/nrajlich/ref/Release/binding.node
+ → /Users/nrajlich/ref/build/default/binding.node
+ → /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node
+ at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13)
+ at Object. (/Users/nrajlich/ref/lib/ref.js:5:47)
+ at Module._compile (module.js:449:26)
+ at Object.Module._extensions..js (module.js:467:10)
+ at Module.load (module.js:356:32)
+ at Function.Module._load (module.js:312:12)
+ ...
+```
+
+The searching for the `.node` file will originate from the first directory in which has a `package.json` file is found.
+
+License
+-------
+
+(The MIT License)
+
+Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/bindings.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/bindings.js
new file mode 100644
index 000000000..727413a19
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/bindings.js
@@ -0,0 +1,221 @@
+/**
+ * Module dependencies.
+ */
+
+var fs = require('fs'),
+ path = require('path'),
+ fileURLToPath = require('file-uri-to-path'),
+ join = path.join,
+ dirname = path.dirname,
+ exists =
+ (fs.accessSync &&
+ function(path) {
+ try {
+ fs.accessSync(path);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }) ||
+ fs.existsSync ||
+ path.existsSync,
+ defaults = {
+ arrow: process.env.NODE_BINDINGS_ARROW || ' → ',
+ compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled',
+ platform: process.platform,
+ arch: process.arch,
+ nodePreGyp:
+ 'node-v' +
+ process.versions.modules +
+ '-' +
+ process.platform +
+ '-' +
+ process.arch,
+ version: process.versions.node,
+ bindings: 'bindings.node',
+ try: [
+ // node-gyp's linked version in the "build" dir
+ ['module_root', 'build', 'bindings'],
+ // node-waf and gyp_addon (a.k.a node-gyp)
+ ['module_root', 'build', 'Debug', 'bindings'],
+ ['module_root', 'build', 'Release', 'bindings'],
+ // Debug files, for development (legacy behavior, remove for node v0.9)
+ ['module_root', 'out', 'Debug', 'bindings'],
+ ['module_root', 'Debug', 'bindings'],
+ // Release files, but manually compiled (legacy behavior, remove for node v0.9)
+ ['module_root', 'out', 'Release', 'bindings'],
+ ['module_root', 'Release', 'bindings'],
+ // Legacy from node-waf, node <= 0.4.x
+ ['module_root', 'build', 'default', 'bindings'],
+ // Production "Release" buildtype binary (meh...)
+ ['module_root', 'compiled', 'version', 'platform', 'arch', 'bindings'],
+ // node-qbs builds
+ ['module_root', 'addon-build', 'release', 'install-root', 'bindings'],
+ ['module_root', 'addon-build', 'debug', 'install-root', 'bindings'],
+ ['module_root', 'addon-build', 'default', 'install-root', 'bindings'],
+ // node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
+ ['module_root', 'lib', 'binding', 'nodePreGyp', 'bindings']
+ ]
+ };
+
+/**
+ * The main `bindings()` function loads the compiled bindings for a given module.
+ * It uses V8's Error API to determine the parent filename that this function is
+ * being invoked from, which is then used to find the root directory.
+ */
+
+function bindings(opts) {
+ // Argument surgery
+ if (typeof opts == 'string') {
+ opts = { bindings: opts };
+ } else if (!opts) {
+ opts = {};
+ }
+
+ // maps `defaults` onto `opts` object
+ Object.keys(defaults).map(function(i) {
+ if (!(i in opts)) opts[i] = defaults[i];
+ });
+
+ // Get the module root
+ if (!opts.module_root) {
+ opts.module_root = exports.getRoot(exports.getFileName());
+ }
+
+ // Ensure the given bindings name ends with .node
+ if (path.extname(opts.bindings) != '.node') {
+ opts.bindings += '.node';
+ }
+
+ // https://github.com/webpack/webpack/issues/4175#issuecomment-342931035
+ var requireFunc =
+ typeof __webpack_require__ === 'function'
+ ? __non_webpack_require__
+ : require;
+
+ var tries = [],
+ i = 0,
+ l = opts.try.length,
+ n,
+ b,
+ err;
+
+ for (; i < l; i++) {
+ n = join.apply(
+ null,
+ opts.try[i].map(function(p) {
+ return opts[p] || p;
+ })
+ );
+ tries.push(n);
+ try {
+ b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
+ if (!opts.path) {
+ b.path = n;
+ }
+ return b;
+ } catch (e) {
+ if (e.code !== 'MODULE_NOT_FOUND' &&
+ e.code !== 'QUALIFIED_PATH_RESOLUTION_FAILED' &&
+ !/not find/i.test(e.message)) {
+ throw e;
+ }
+ }
+ }
+
+ err = new Error(
+ 'Could not locate the bindings file. Tried:\n' +
+ tries
+ .map(function(a) {
+ return opts.arrow + a;
+ })
+ .join('\n')
+ );
+ err.tries = tries;
+ throw err;
+}
+module.exports = exports = bindings;
+
+/**
+ * Gets the filename of the JavaScript file that invokes this function.
+ * Used to help find the root directory of a module.
+ * Optionally accepts an filename argument to skip when searching for the invoking filename
+ */
+
+exports.getFileName = function getFileName(calling_file) {
+ var origPST = Error.prepareStackTrace,
+ origSTL = Error.stackTraceLimit,
+ dummy = {},
+ fileName;
+
+ Error.stackTraceLimit = 10;
+
+ Error.prepareStackTrace = function(e, st) {
+ for (var i = 0, l = st.length; i < l; i++) {
+ fileName = st[i].getFileName();
+ if (fileName !== __filename) {
+ if (calling_file) {
+ if (fileName !== calling_file) {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+ }
+ };
+
+ // run the 'prepareStackTrace' function above
+ Error.captureStackTrace(dummy);
+ dummy.stack;
+
+ // cleanup
+ Error.prepareStackTrace = origPST;
+ Error.stackTraceLimit = origSTL;
+
+ // handle filename that starts with "file://"
+ var fileSchema = 'file://';
+ if (fileName.indexOf(fileSchema) === 0) {
+ fileName = fileURLToPath(fileName);
+ }
+
+ return fileName;
+};
+
+/**
+ * Gets the root directory of a module, given an arbitrary filename
+ * somewhere in the module tree. The "root directory" is the directory
+ * containing the `package.json` file.
+ *
+ * In: /home/nate/node-native-module/lib/index.js
+ * Out: /home/nate/node-native-module
+ */
+
+exports.getRoot = function getRoot(file) {
+ var dir = dirname(file),
+ prev;
+ while (true) {
+ if (dir === '.') {
+ // Avoids an infinite loop in rare cases, like the REPL
+ dir = process.cwd();
+ }
+ if (
+ exists(join(dir, 'package.json')) ||
+ exists(join(dir, 'node_modules'))
+ ) {
+ // Found the 'package.json' file or 'node_modules' dir; we're done
+ return dir;
+ }
+ if (prev === dir) {
+ // Got to the top
+ throw new Error(
+ 'Could not find module root given file: "' +
+ file +
+ '". Do you have a `package.json` file? '
+ );
+ }
+ // Try the parent dir next
+ prev = dir;
+ dir = join(dir, '..');
+ }
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/package.json
new file mode 100644
index 000000000..d027ee78a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bindings/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "bindings",
+ "description": "Helper module for loading your native module's .node file",
+ "keywords": [
+ "native",
+ "addon",
+ "bindings",
+ "gyp",
+ "waf",
+ "c",
+ "c++"
+ ],
+ "version": "1.5.0",
+ "author": "Nathan Rajlich (http://tootallnate.net)",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/TooTallNate/node-bindings.git"
+ },
+ "main": "./bindings.js",
+ "bugs": {
+ "url": "https://github.com/TooTallNate/node-bindings/issues"
+ },
+ "homepage": "https://github.com/TooTallNate/node-bindings",
+ "license": "MIT",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/.travis.yml b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/.travis.yml
new file mode 100644
index 000000000..016eaf556
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/.travis.yml
@@ -0,0 +1,17 @@
+sudo: false
+arch:
+ - amd64
+ - ppc64le
+language: node_js
+node_js:
+ - '6'
+ - '8'
+ - '10'
+ - '12'
+ - '14'
+ - '15'
+ - lts/*
+notifications:
+ email:
+ - rod@vagg.org
+ - matteo.collina@gmail.com
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/BufferList.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/BufferList.js
new file mode 100644
index 000000000..471ee7788
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/BufferList.js
@@ -0,0 +1,396 @@
+'use strict'
+
+const { Buffer } = require('buffer')
+const symbol = Symbol.for('BufferList')
+
+function BufferList (buf) {
+ if (!(this instanceof BufferList)) {
+ return new BufferList(buf)
+ }
+
+ BufferList._init.call(this, buf)
+}
+
+BufferList._init = function _init (buf) {
+ Object.defineProperty(this, symbol, { value: true })
+
+ this._bufs = []
+ this.length = 0
+
+ if (buf) {
+ this.append(buf)
+ }
+}
+
+BufferList.prototype._new = function _new (buf) {
+ return new BufferList(buf)
+}
+
+BufferList.prototype._offset = function _offset (offset) {
+ if (offset === 0) {
+ return [0, 0]
+ }
+
+ let tot = 0
+
+ for (let i = 0; i < this._bufs.length; i++) {
+ const _t = tot + this._bufs[i].length
+ if (offset < _t || i === this._bufs.length - 1) {
+ return [i, offset - tot]
+ }
+ tot = _t
+ }
+}
+
+BufferList.prototype._reverseOffset = function (blOffset) {
+ const bufferId = blOffset[0]
+ let offset = blOffset[1]
+
+ for (let i = 0; i < bufferId; i++) {
+ offset += this._bufs[i].length
+ }
+
+ return offset
+}
+
+BufferList.prototype.get = function get (index) {
+ if (index > this.length || index < 0) {
+ return undefined
+ }
+
+ const offset = this._offset(index)
+
+ return this._bufs[offset[0]][offset[1]]
+}
+
+BufferList.prototype.slice = function slice (start, end) {
+ if (typeof start === 'number' && start < 0) {
+ start += this.length
+ }
+
+ if (typeof end === 'number' && end < 0) {
+ end += this.length
+ }
+
+ return this.copy(null, 0, start, end)
+}
+
+BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) {
+ if (typeof srcStart !== 'number' || srcStart < 0) {
+ srcStart = 0
+ }
+
+ if (typeof srcEnd !== 'number' || srcEnd > this.length) {
+ srcEnd = this.length
+ }
+
+ if (srcStart >= this.length) {
+ return dst || Buffer.alloc(0)
+ }
+
+ if (srcEnd <= 0) {
+ return dst || Buffer.alloc(0)
+ }
+
+ const copy = !!dst
+ const off = this._offset(srcStart)
+ const len = srcEnd - srcStart
+ let bytes = len
+ let bufoff = (copy && dstStart) || 0
+ let start = off[1]
+
+ // copy/slice everything
+ if (srcStart === 0 && srcEnd === this.length) {
+ if (!copy) {
+ // slice, but full concat if multiple buffers
+ return this._bufs.length === 1
+ ? this._bufs[0]
+ : Buffer.concat(this._bufs, this.length)
+ }
+
+ // copy, need to copy individual buffers
+ for (let i = 0; i < this._bufs.length; i++) {
+ this._bufs[i].copy(dst, bufoff)
+ bufoff += this._bufs[i].length
+ }
+
+ return dst
+ }
+
+ // easy, cheap case where it's a subset of one of the buffers
+ if (bytes <= this._bufs[off[0]].length - start) {
+ return copy
+ ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes)
+ : this._bufs[off[0]].slice(start, start + bytes)
+ }
+
+ if (!copy) {
+ // a slice, we need something to copy in to
+ dst = Buffer.allocUnsafe(len)
+ }
+
+ for (let i = off[0]; i < this._bufs.length; i++) {
+ const l = this._bufs[i].length - start
+
+ if (bytes > l) {
+ this._bufs[i].copy(dst, bufoff, start)
+ bufoff += l
+ } else {
+ this._bufs[i].copy(dst, bufoff, start, start + bytes)
+ bufoff += l
+ break
+ }
+
+ bytes -= l
+
+ if (start) {
+ start = 0
+ }
+ }
+
+ // safeguard so that we don't return uninitialized memory
+ if (dst.length > bufoff) return dst.slice(0, bufoff)
+
+ return dst
+}
+
+BufferList.prototype.shallowSlice = function shallowSlice (start, end) {
+ start = start || 0
+ end = typeof end !== 'number' ? this.length : end
+
+ if (start < 0) {
+ start += this.length
+ }
+
+ if (end < 0) {
+ end += this.length
+ }
+
+ if (start === end) {
+ return this._new()
+ }
+
+ const startOffset = this._offset(start)
+ const endOffset = this._offset(end)
+ const buffers = this._bufs.slice(startOffset[0], endOffset[0] + 1)
+
+ if (endOffset[1] === 0) {
+ buffers.pop()
+ } else {
+ buffers[buffers.length - 1] = buffers[buffers.length - 1].slice(0, endOffset[1])
+ }
+
+ if (startOffset[1] !== 0) {
+ buffers[0] = buffers[0].slice(startOffset[1])
+ }
+
+ return this._new(buffers)
+}
+
+BufferList.prototype.toString = function toString (encoding, start, end) {
+ return this.slice(start, end).toString(encoding)
+}
+
+BufferList.prototype.consume = function consume (bytes) {
+ // first, normalize the argument, in accordance with how Buffer does it
+ bytes = Math.trunc(bytes)
+ // do nothing if not a positive number
+ if (Number.isNaN(bytes) || bytes <= 0) return this
+
+ while (this._bufs.length) {
+ if (bytes >= this._bufs[0].length) {
+ bytes -= this._bufs[0].length
+ this.length -= this._bufs[0].length
+ this._bufs.shift()
+ } else {
+ this._bufs[0] = this._bufs[0].slice(bytes)
+ this.length -= bytes
+ break
+ }
+ }
+
+ return this
+}
+
+BufferList.prototype.duplicate = function duplicate () {
+ const copy = this._new()
+
+ for (let i = 0; i < this._bufs.length; i++) {
+ copy.append(this._bufs[i])
+ }
+
+ return copy
+}
+
+BufferList.prototype.append = function append (buf) {
+ if (buf == null) {
+ return this
+ }
+
+ if (buf.buffer) {
+ // append a view of the underlying ArrayBuffer
+ this._appendBuffer(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength))
+ } else if (Array.isArray(buf)) {
+ for (let i = 0; i < buf.length; i++) {
+ this.append(buf[i])
+ }
+ } else if (this._isBufferList(buf)) {
+ // unwrap argument into individual BufferLists
+ for (let i = 0; i < buf._bufs.length; i++) {
+ this.append(buf._bufs[i])
+ }
+ } else {
+ // coerce number arguments to strings, since Buffer(number) does
+ // uninitialized memory allocation
+ if (typeof buf === 'number') {
+ buf = buf.toString()
+ }
+
+ this._appendBuffer(Buffer.from(buf))
+ }
+
+ return this
+}
+
+BufferList.prototype._appendBuffer = function appendBuffer (buf) {
+ this._bufs.push(buf)
+ this.length += buf.length
+}
+
+BufferList.prototype.indexOf = function (search, offset, encoding) {
+ if (encoding === undefined && typeof offset === 'string') {
+ encoding = offset
+ offset = undefined
+ }
+
+ if (typeof search === 'function' || Array.isArray(search)) {
+ throw new TypeError('The "value" argument must be one of type string, Buffer, BufferList, or Uint8Array.')
+ } else if (typeof search === 'number') {
+ search = Buffer.from([search])
+ } else if (typeof search === 'string') {
+ search = Buffer.from(search, encoding)
+ } else if (this._isBufferList(search)) {
+ search = search.slice()
+ } else if (Array.isArray(search.buffer)) {
+ search = Buffer.from(search.buffer, search.byteOffset, search.byteLength)
+ } else if (!Buffer.isBuffer(search)) {
+ search = Buffer.from(search)
+ }
+
+ offset = Number(offset || 0)
+
+ if (isNaN(offset)) {
+ offset = 0
+ }
+
+ if (offset < 0) {
+ offset = this.length + offset
+ }
+
+ if (offset < 0) {
+ offset = 0
+ }
+
+ if (search.length === 0) {
+ return offset > this.length ? this.length : offset
+ }
+
+ const blOffset = this._offset(offset)
+ let blIndex = blOffset[0] // index of which internal buffer we're working on
+ let buffOffset = blOffset[1] // offset of the internal buffer we're working on
+
+ // scan over each buffer
+ for (; blIndex < this._bufs.length; blIndex++) {
+ const buff = this._bufs[blIndex]
+
+ while (buffOffset < buff.length) {
+ const availableWindow = buff.length - buffOffset
+
+ if (availableWindow >= search.length) {
+ const nativeSearchResult = buff.indexOf(search, buffOffset)
+
+ if (nativeSearchResult !== -1) {
+ return this._reverseOffset([blIndex, nativeSearchResult])
+ }
+
+ buffOffset = buff.length - search.length + 1 // end of native search window
+ } else {
+ const revOffset = this._reverseOffset([blIndex, buffOffset])
+
+ if (this._match(revOffset, search)) {
+ return revOffset
+ }
+
+ buffOffset++
+ }
+ }
+
+ buffOffset = 0
+ }
+
+ return -1
+}
+
+BufferList.prototype._match = function (offset, search) {
+ if (this.length - offset < search.length) {
+ return false
+ }
+
+ for (let searchOffset = 0; searchOffset < search.length; searchOffset++) {
+ if (this.get(offset + searchOffset) !== search[searchOffset]) {
+ return false
+ }
+ }
+ return true
+}
+
+;(function () {
+ const methods = {
+ readDoubleBE: 8,
+ readDoubleLE: 8,
+ readFloatBE: 4,
+ readFloatLE: 4,
+ readInt32BE: 4,
+ readInt32LE: 4,
+ readUInt32BE: 4,
+ readUInt32LE: 4,
+ readInt16BE: 2,
+ readInt16LE: 2,
+ readUInt16BE: 2,
+ readUInt16LE: 2,
+ readInt8: 1,
+ readUInt8: 1,
+ readIntBE: null,
+ readIntLE: null,
+ readUIntBE: null,
+ readUIntLE: null
+ }
+
+ for (const m in methods) {
+ (function (m) {
+ if (methods[m] === null) {
+ BufferList.prototype[m] = function (offset, byteLength) {
+ return this.slice(offset, offset + byteLength)[m](0, byteLength)
+ }
+ } else {
+ BufferList.prototype[m] = function (offset = 0) {
+ return this.slice(offset, offset + methods[m])[m](0)
+ }
+ }
+ }(m))
+ }
+}())
+
+// Used internally by the class and also as an indicator of this object being
+// a `BufferList`. It's not possible to use `instanceof BufferList` in a browser
+// environment because there could be multiple different copies of the
+// BufferList class and some `BufferList`s might be `BufferList`s.
+BufferList.prototype._isBufferList = function _isBufferList (b) {
+ return b instanceof BufferList || BufferList.isBufferList(b)
+}
+
+BufferList.isBufferList = function isBufferList (b) {
+ return b != null && b[symbol]
+}
+
+module.exports = BufferList
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/LICENSE.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/LICENSE.md
new file mode 100644
index 000000000..ecbe51637
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/LICENSE.md
@@ -0,0 +1,13 @@
+The MIT License (MIT)
+=====================
+
+Copyright (c) 2013-2019 bl contributors
+----------------------------------
+
+*bl contributors listed at *
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/README.md
new file mode 100644
index 000000000..9680b1dcb
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/README.md
@@ -0,0 +1,247 @@
+# bl *(BufferList)*
+
+[](https://travis-ci.com/rvagg/bl/)
+
+**A Node.js Buffer list collector, reader and streamer thingy.**
+
+[](https://nodei.co/npm/bl/)
+
+**bl** is a storage object for collections of Node Buffers, exposing them with the main Buffer readable API. Also works as a duplex stream so you can collect buffers from a stream that emits them and emit buffers to a stream that consumes them!
+
+The original buffers are kept intact and copies are only done as necessary. Any reads that require the use of a single original buffer will return a slice of that buffer only (which references the same memory as the original buffer). Reads that span buffers perform concatenation as required and return the results transparently.
+
+```js
+const { BufferList } = require('bl')
+
+const bl = new BufferList()
+bl.append(Buffer.from('abcd'))
+bl.append(Buffer.from('efg'))
+bl.append('hi') // bl will also accept & convert Strings
+bl.append(Buffer.from('j'))
+bl.append(Buffer.from([ 0x3, 0x4 ]))
+
+console.log(bl.length) // 12
+
+console.log(bl.slice(0, 10).toString('ascii')) // 'abcdefghij'
+console.log(bl.slice(3, 10).toString('ascii')) // 'defghij'
+console.log(bl.slice(3, 6).toString('ascii')) // 'def'
+console.log(bl.slice(3, 8).toString('ascii')) // 'defgh'
+console.log(bl.slice(5, 10).toString('ascii')) // 'fghij'
+
+console.log(bl.indexOf('def')) // 3
+console.log(bl.indexOf('asdf')) // -1
+
+// or just use toString!
+console.log(bl.toString()) // 'abcdefghij\u0003\u0004'
+console.log(bl.toString('ascii', 3, 8)) // 'defgh'
+console.log(bl.toString('ascii', 5, 10)) // 'fghij'
+
+// other standard Buffer readables
+console.log(bl.readUInt16BE(10)) // 0x0304
+console.log(bl.readUInt16LE(10)) // 0x0403
+```
+
+Give it a callback in the constructor and use it just like **[concat-stream](https://github.com/maxogden/node-concat-stream)**:
+
+```js
+const { BufferListStream } = require('bl')
+const fs = require('fs')
+
+fs.createReadStream('README.md')
+ .pipe(BufferListStream((err, data) => { // note 'new' isn't strictly required
+ // `data` is a complete Buffer object containing the full data
+ console.log(data.toString())
+ }))
+```
+
+Note that when you use the *callback* method like this, the resulting `data` parameter is a concatenation of all `Buffer` objects in the list. If you want to avoid the overhead of this concatenation (in cases of extreme performance consciousness), then avoid the *callback* method and just listen to `'end'` instead, like a standard Stream.
+
+Or to fetch a URL using [hyperquest](https://github.com/substack/hyperquest) (should work with [request](http://github.com/mikeal/request) and even plain Node http too!):
+
+```js
+const hyperquest = require('hyperquest')
+const { BufferListStream } = require('bl')
+
+const url = 'https://raw.github.com/rvagg/bl/master/README.md'
+
+hyperquest(url).pipe(BufferListStream((err, data) => {
+ console.log(data.toString())
+}))
+```
+
+Or, use it as a readable stream to recompose a list of Buffers to an output source:
+
+```js
+const { BufferListStream } = require('bl')
+const fs = require('fs')
+
+var bl = new BufferListStream()
+bl.append(Buffer.from('abcd'))
+bl.append(Buffer.from('efg'))
+bl.append(Buffer.from('hi'))
+bl.append(Buffer.from('j'))
+
+bl.pipe(fs.createWriteStream('gibberish.txt'))
+```
+
+## API
+
+ * new BufferList([ buf ])
+ * BufferList.isBufferList(obj)
+ * bl.length
+ * bl.append(buffer)
+ * bl.get(index)
+ * bl.indexOf(value[, byteOffset][, encoding])
+ * bl.slice([ start[, end ] ])
+ * bl.shallowSlice([ start[, end ] ])
+ * bl.copy(dest, [ destStart, [ srcStart [, srcEnd ] ] ])
+ * bl.duplicate()
+ * bl.consume(bytes)
+ * bl.toString([encoding, [ start, [ end ]]])
+ * bl.readDoubleBE() , bl.readDoubleLE() , bl.readFloatBE() , bl.readFloatLE() , bl.readInt32BE() , bl.readInt32LE() , bl.readUInt32BE() , bl.readUInt32LE() , bl.readInt16BE() , bl.readInt16LE() , bl.readUInt16BE() , bl.readUInt16LE() , bl.readInt8() , bl.readUInt8()
+ * new BufferListStream([ callback ])
+
+--------------------------------------------------------
+
+### new BufferList([ Buffer | Buffer array | BufferList | BufferList array | String ])
+No arguments are _required_ for the constructor, but you can initialise the list by passing in a single `Buffer` object or an array of `Buffer` objects.
+
+`new` is not strictly required, if you don't instantiate a new object, it will be done automatically for you so you can create a new instance simply with:
+
+```js
+const { BufferList } = require('bl')
+const bl = BufferList()
+
+// equivalent to:
+
+const { BufferList } = require('bl')
+const bl = new BufferList()
+```
+
+--------------------------------------------------------
+
+### BufferList.isBufferList(obj)
+Determines if the passed object is a `BufferList`. It will return `true` if the passed object is an instance of `BufferList` **or** `BufferListStream` and `false` otherwise.
+
+N.B. this won't return `true` for `BufferList` or `BufferListStream` instances created by versions of this library before this static method was added.
+
+--------------------------------------------------------
+
+### bl.length
+Get the length of the list in bytes. This is the sum of the lengths of all of the buffers contained in the list, minus any initial offset for a semi-consumed buffer at the beginning. Should accurately represent the total number of bytes that can be read from the list.
+
+--------------------------------------------------------
+
+### bl.append(Buffer | Buffer array | BufferList | BufferList array | String)
+`append(buffer)` adds an additional buffer or BufferList to the internal list. `this` is returned so it can be chained.
+
+--------------------------------------------------------
+
+### bl.get(index)
+`get()` will return the byte at the specified index.
+
+--------------------------------------------------------
+
+### bl.indexOf(value[, byteOffset][, encoding])
+`get()` will return the byte at the specified index.
+`indexOf()` method returns the first index at which a given element can be found in the BufferList, or -1 if it is not present.
+
+--------------------------------------------------------
+
+### bl.slice([ start, [ end ] ])
+`slice()` returns a new `Buffer` object containing the bytes within the range specified. Both `start` and `end` are optional and will default to the beginning and end of the list respectively.
+
+If the requested range spans a single internal buffer then a slice of that buffer will be returned which shares the original memory range of that Buffer. If the range spans multiple buffers then copy operations will likely occur to give you a uniform Buffer.
+
+--------------------------------------------------------
+
+### bl.shallowSlice([ start, [ end ] ])
+`shallowSlice()` returns a new `BufferList` object containing the bytes within the range specified. Both `start` and `end` are optional and will default to the beginning and end of the list respectively.
+
+No copies will be performed. All buffers in the result share memory with the original list.
+
+--------------------------------------------------------
+
+### bl.copy(dest, [ destStart, [ srcStart [, srcEnd ] ] ])
+`copy()` copies the content of the list in the `dest` buffer, starting from `destStart` and containing the bytes within the range specified with `srcStart` to `srcEnd`. `destStart`, `start` and `end` are optional and will default to the beginning of the `dest` buffer, and the beginning and end of the list respectively.
+
+--------------------------------------------------------
+
+### bl.duplicate()
+`duplicate()` performs a **shallow-copy** of the list. The internal Buffers remains the same, so if you change the underlying Buffers, the change will be reflected in both the original and the duplicate. This method is needed if you want to call `consume()` or `pipe()` and still keep the original list.Example:
+
+```js
+var bl = new BufferListStream()
+
+bl.append('hello')
+bl.append(' world')
+bl.append('\n')
+
+bl.duplicate().pipe(process.stdout, { end: false })
+
+console.log(bl.toString())
+```
+
+--------------------------------------------------------
+
+### bl.consume(bytes)
+`consume()` will shift bytes *off the start of the list*. The number of bytes consumed don't need to line up with the sizes of the internal Buffers—initial offsets will be calculated accordingly in order to give you a consistent view of the data.
+
+--------------------------------------------------------
+
+### bl.toString([encoding, [ start, [ end ]]])
+`toString()` will return a string representation of the buffer. The optional `start` and `end` arguments are passed on to `slice()`, while the `encoding` is passed on to `toString()` of the resulting Buffer. See the [Buffer#toString()](http://nodejs.org/docs/latest/api/buffer.html#buffer_buf_tostring_encoding_start_end) documentation for more information.
+
+--------------------------------------------------------
+
+### bl.readDoubleBE(), bl.readDoubleLE(), bl.readFloatBE(), bl.readFloatLE(), bl.readInt32BE(), bl.readInt32LE(), bl.readUInt32BE(), bl.readUInt32LE(), bl.readInt16BE(), bl.readInt16LE(), bl.readUInt16BE(), bl.readUInt16LE(), bl.readInt8(), bl.readUInt8()
+
+All of the standard byte-reading methods of the `Buffer` interface are implemented and will operate across internal Buffer boundaries transparently.
+
+See the [Buffer](http://nodejs.org/docs/latest/api/buffer.html) documentation for how these work.
+
+--------------------------------------------------------
+
+### new BufferListStream([ callback | Buffer | Buffer array | BufferList | BufferList array | String ])
+**BufferListStream** is a Node **[Duplex Stream](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_duplex)**, so it can be read from and written to like a standard Node stream. You can also `pipe()` to and from a **BufferListStream** instance.
+
+The constructor takes an optional callback, if supplied, the callback will be called with an error argument followed by a reference to the **bl** instance, when `bl.end()` is called (i.e. from a piped stream). This is a convenient method of collecting the entire contents of a stream, particularly when the stream is *chunky*, such as a network stream.
+
+Normally, no arguments are required for the constructor, but you can initialise the list by passing in a single `Buffer` object or an array of `Buffer` object.
+
+`new` is not strictly required, if you don't instantiate a new object, it will be done automatically for you so you can create a new instance simply with:
+
+```js
+const { BufferListStream } = require('bl')
+const bl = BufferListStream()
+
+// equivalent to:
+
+const { BufferListStream } = require('bl')
+const bl = new BufferListStream()
+```
+
+N.B. For backwards compatibility reasons, `BufferListStream` is the **default** export when you `require('bl')`:
+
+```js
+const { BufferListStream } = require('bl')
+// equivalent to:
+const BufferListStream = require('bl')
+```
+
+--------------------------------------------------------
+
+## Contributors
+
+**bl** is brought to you by the following hackers:
+
+ * [Rod Vagg](https://github.com/rvagg)
+ * [Matteo Collina](https://github.com/mcollina)
+ * [Jarett Cruger](https://github.com/jcrugzz)
+
+
+## License & copyright
+
+Copyright (c) 2013-2019 bl contributors (listed above).
+
+bl is licensed under the MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/bl.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/bl.js
new file mode 100644
index 000000000..40228f879
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/bl.js
@@ -0,0 +1,84 @@
+'use strict'
+
+const DuplexStream = require('readable-stream').Duplex
+const inherits = require('inherits')
+const BufferList = require('./BufferList')
+
+function BufferListStream (callback) {
+ if (!(this instanceof BufferListStream)) {
+ return new BufferListStream(callback)
+ }
+
+ if (typeof callback === 'function') {
+ this._callback = callback
+
+ const piper = function piper (err) {
+ if (this._callback) {
+ this._callback(err)
+ this._callback = null
+ }
+ }.bind(this)
+
+ this.on('pipe', function onPipe (src) {
+ src.on('error', piper)
+ })
+ this.on('unpipe', function onUnpipe (src) {
+ src.removeListener('error', piper)
+ })
+
+ callback = null
+ }
+
+ BufferList._init.call(this, callback)
+ DuplexStream.call(this)
+}
+
+inherits(BufferListStream, DuplexStream)
+Object.assign(BufferListStream.prototype, BufferList.prototype)
+
+BufferListStream.prototype._new = function _new (callback) {
+ return new BufferListStream(callback)
+}
+
+BufferListStream.prototype._write = function _write (buf, encoding, callback) {
+ this._appendBuffer(buf)
+
+ if (typeof callback === 'function') {
+ callback()
+ }
+}
+
+BufferListStream.prototype._read = function _read (size) {
+ if (!this.length) {
+ return this.push(null)
+ }
+
+ size = Math.min(size, this.length)
+ this.push(this.slice(0, size))
+ this.consume(size)
+}
+
+BufferListStream.prototype.end = function end (chunk) {
+ DuplexStream.prototype.end.call(this, chunk)
+
+ if (this._callback) {
+ this._callback(null, this.slice())
+ this._callback = null
+ }
+}
+
+BufferListStream.prototype._destroy = function _destroy (err, cb) {
+ this._bufs.length = 0
+ this.length = 0
+ cb(err)
+}
+
+BufferListStream.prototype._isBufferList = function _isBufferList (b) {
+ return b instanceof BufferListStream || b instanceof BufferList || BufferListStream.isBufferList(b)
+}
+
+BufferListStream.isBufferList = BufferList.isBufferList
+
+module.exports = BufferListStream
+module.exports.BufferListStream = BufferListStream
+module.exports.BufferList = BufferList
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/package.json
new file mode 100644
index 000000000..3b2be3f48
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "bl",
+ "version": "4.1.0",
+ "description": "Buffer List: collect buffers and access with a standard readable Buffer interface, streamable too!",
+ "license": "MIT",
+ "main": "bl.js",
+ "scripts": {
+ "lint": "standard *.js test/*.js",
+ "test": "npm run lint && node test/test.js | faucet"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/rvagg/bl.git"
+ },
+ "homepage": "https://github.com/rvagg/bl",
+ "authors": [
+ "Rod Vagg (https://github.com/rvagg)",
+ "Matteo Collina (https://github.com/mcollina)",
+ "Jarett Cruger (https://github.com/jcrugzz)"
+ ],
+ "keywords": [
+ "buffer",
+ "buffers",
+ "stream",
+ "awesomesauce"
+ ],
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ },
+ "devDependencies": {
+ "faucet": "~0.0.1",
+ "standard": "^14.3.0",
+ "tape": "^4.11.0"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/convert.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/convert.js
new file mode 100644
index 000000000..9f3e23599
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/convert.js
@@ -0,0 +1,21 @@
+'use strict'
+
+const tape = require('tape')
+const { BufferList, BufferListStream } = require('../')
+const { Buffer } = require('buffer')
+
+tape('convert from BufferList to BufferListStream', (t) => {
+ const data = Buffer.from(`TEST-${Date.now()}`)
+ const bl = new BufferList(data)
+ const bls = new BufferListStream(bl)
+ t.ok(bl.slice().equals(bls.slice()))
+ t.end()
+})
+
+tape('convert from BufferListStream to BufferList', (t) => {
+ const data = Buffer.from(`TEST-${Date.now()}`)
+ const bls = new BufferListStream(data)
+ const bl = new BufferList(bls)
+ t.ok(bl.slice().equals(bls.slice()))
+ t.end()
+})
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/indexOf.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/indexOf.js
new file mode 100644
index 000000000..62dcb01f3
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/indexOf.js
@@ -0,0 +1,492 @@
+'use strict'
+
+const tape = require('tape')
+const BufferList = require('../')
+const { Buffer } = require('buffer')
+
+tape('indexOf single byte needle', (t) => {
+ const bl = new BufferList(['abcdefg', 'abcdefg', '12345'])
+
+ t.equal(bl.indexOf('e'), 4)
+ t.equal(bl.indexOf('e', 5), 11)
+ t.equal(bl.indexOf('e', 12), -1)
+ t.equal(bl.indexOf('5'), 18)
+
+ t.end()
+})
+
+tape('indexOf multiple byte needle', (t) => {
+ const bl = new BufferList(['abcdefg', 'abcdefg'])
+
+ t.equal(bl.indexOf('ef'), 4)
+ t.equal(bl.indexOf('ef', 5), 11)
+
+ t.end()
+})
+
+tape('indexOf multiple byte needles across buffer boundaries', (t) => {
+ const bl = new BufferList(['abcdefg', 'abcdefg'])
+
+ t.equal(bl.indexOf('fgabc'), 5)
+
+ t.end()
+})
+
+tape('indexOf takes a Uint8Array search', (t) => {
+ const bl = new BufferList(['abcdefg', 'abcdefg'])
+ const search = new Uint8Array([102, 103, 97, 98, 99]) // fgabc
+
+ t.equal(bl.indexOf(search), 5)
+
+ t.end()
+})
+
+tape('indexOf takes a buffer list search', (t) => {
+ const bl = new BufferList(['abcdefg', 'abcdefg'])
+ const search = new BufferList('fgabc')
+
+ t.equal(bl.indexOf(search), 5)
+
+ t.end()
+})
+
+tape('indexOf a zero byte needle', (t) => {
+ const b = new BufferList('abcdef')
+ const bufEmpty = Buffer.from('')
+
+ t.equal(b.indexOf(''), 0)
+ t.equal(b.indexOf('', 1), 1)
+ t.equal(b.indexOf('', b.length + 1), b.length)
+ t.equal(b.indexOf('', Infinity), b.length)
+ t.equal(b.indexOf(bufEmpty), 0)
+ t.equal(b.indexOf(bufEmpty, 1), 1)
+ t.equal(b.indexOf(bufEmpty, b.length + 1), b.length)
+ t.equal(b.indexOf(bufEmpty, Infinity), b.length)
+
+ t.end()
+})
+
+tape('indexOf buffers smaller and larger than the needle', (t) => {
+ const bl = new BufferList(['abcdefg', 'a', 'bcdefg', 'a', 'bcfgab'])
+
+ t.equal(bl.indexOf('fgabc'), 5)
+ t.equal(bl.indexOf('fgabc', 6), 12)
+ t.equal(bl.indexOf('fgabc', 13), -1)
+
+ t.end()
+})
+
+// only present in node 6+
+;(process.version.substr(1).split('.')[0] >= 6) && tape('indexOf latin1 and binary encoding', (t) => {
+ const b = new BufferList('abcdef')
+
+ // test latin1 encoding
+ t.equal(
+ new BufferList(Buffer.from(b.toString('latin1'), 'latin1'))
+ .indexOf('d', 0, 'latin1'),
+ 3
+ )
+ t.equal(
+ new BufferList(Buffer.from(b.toString('latin1'), 'latin1'))
+ .indexOf(Buffer.from('d', 'latin1'), 0, 'latin1'),
+ 3
+ )
+ t.equal(
+ new BufferList(Buffer.from('aa\u00e8aa', 'latin1'))
+ .indexOf('\u00e8', 'latin1'),
+ 2
+ )
+ t.equal(
+ new BufferList(Buffer.from('\u00e8', 'latin1'))
+ .indexOf('\u00e8', 'latin1'),
+ 0
+ )
+ t.equal(
+ new BufferList(Buffer.from('\u00e8', 'latin1'))
+ .indexOf(Buffer.from('\u00e8', 'latin1'), 'latin1'),
+ 0
+ )
+
+ // test binary encoding
+ t.equal(
+ new BufferList(Buffer.from(b.toString('binary'), 'binary'))
+ .indexOf('d', 0, 'binary'),
+ 3
+ )
+ t.equal(
+ new BufferList(Buffer.from(b.toString('binary'), 'binary'))
+ .indexOf(Buffer.from('d', 'binary'), 0, 'binary'),
+ 3
+ )
+ t.equal(
+ new BufferList(Buffer.from('aa\u00e8aa', 'binary'))
+ .indexOf('\u00e8', 'binary'),
+ 2
+ )
+ t.equal(
+ new BufferList(Buffer.from('\u00e8', 'binary'))
+ .indexOf('\u00e8', 'binary'),
+ 0
+ )
+ t.equal(
+ new BufferList(Buffer.from('\u00e8', 'binary'))
+ .indexOf(Buffer.from('\u00e8', 'binary'), 'binary'),
+ 0
+ )
+
+ t.end()
+})
+
+tape('indexOf the entire nodejs10 buffer test suite', (t) => {
+ const b = new BufferList('abcdef')
+ const bufA = Buffer.from('a')
+ const bufBc = Buffer.from('bc')
+ const bufF = Buffer.from('f')
+ const bufZ = Buffer.from('z')
+
+ const stringComparison = 'abcdef'
+
+ t.equal(b.indexOf('a'), 0)
+ t.equal(b.indexOf('a', 1), -1)
+ t.equal(b.indexOf('a', -1), -1)
+ t.equal(b.indexOf('a', -4), -1)
+ t.equal(b.indexOf('a', -b.length), 0)
+ t.equal(b.indexOf('a', NaN), 0)
+ t.equal(b.indexOf('a', -Infinity), 0)
+ t.equal(b.indexOf('a', Infinity), -1)
+ t.equal(b.indexOf('bc'), 1)
+ t.equal(b.indexOf('bc', 2), -1)
+ t.equal(b.indexOf('bc', -1), -1)
+ t.equal(b.indexOf('bc', -3), -1)
+ t.equal(b.indexOf('bc', -5), 1)
+ t.equal(b.indexOf('bc', NaN), 1)
+ t.equal(b.indexOf('bc', -Infinity), 1)
+ t.equal(b.indexOf('bc', Infinity), -1)
+ t.equal(b.indexOf('f'), b.length - 1)
+ t.equal(b.indexOf('z'), -1)
+
+ // empty search tests
+ t.equal(b.indexOf(bufA), 0)
+ t.equal(b.indexOf(bufA, 1), -1)
+ t.equal(b.indexOf(bufA, -1), -1)
+ t.equal(b.indexOf(bufA, -4), -1)
+ t.equal(b.indexOf(bufA, -b.length), 0)
+ t.equal(b.indexOf(bufA, NaN), 0)
+ t.equal(b.indexOf(bufA, -Infinity), 0)
+ t.equal(b.indexOf(bufA, Infinity), -1)
+ t.equal(b.indexOf(bufBc), 1)
+ t.equal(b.indexOf(bufBc, 2), -1)
+ t.equal(b.indexOf(bufBc, -1), -1)
+ t.equal(b.indexOf(bufBc, -3), -1)
+ t.equal(b.indexOf(bufBc, -5), 1)
+ t.equal(b.indexOf(bufBc, NaN), 1)
+ t.equal(b.indexOf(bufBc, -Infinity), 1)
+ t.equal(b.indexOf(bufBc, Infinity), -1)
+ t.equal(b.indexOf(bufF), b.length - 1)
+ t.equal(b.indexOf(bufZ), -1)
+ t.equal(b.indexOf(0x61), 0)
+ t.equal(b.indexOf(0x61, 1), -1)
+ t.equal(b.indexOf(0x61, -1), -1)
+ t.equal(b.indexOf(0x61, -4), -1)
+ t.equal(b.indexOf(0x61, -b.length), 0)
+ t.equal(b.indexOf(0x61, NaN), 0)
+ t.equal(b.indexOf(0x61, -Infinity), 0)
+ t.equal(b.indexOf(0x61, Infinity), -1)
+ t.equal(b.indexOf(0x0), -1)
+
+ // test offsets
+ t.equal(b.indexOf('d', 2), 3)
+ t.equal(b.indexOf('f', 5), 5)
+ t.equal(b.indexOf('f', -1), 5)
+ t.equal(b.indexOf('f', 6), -1)
+
+ t.equal(b.indexOf(Buffer.from('d'), 2), 3)
+ t.equal(b.indexOf(Buffer.from('f'), 5), 5)
+ t.equal(b.indexOf(Buffer.from('f'), -1), 5)
+ t.equal(b.indexOf(Buffer.from('f'), 6), -1)
+
+ t.equal(Buffer.from('ff').indexOf(Buffer.from('f'), 1, 'ucs2'), -1)
+
+ // test invalid and uppercase encoding
+ t.equal(b.indexOf('b', 'utf8'), 1)
+ t.equal(b.indexOf('b', 'UTF8'), 1)
+ t.equal(b.indexOf('62', 'HEX'), 1)
+ t.throws(() => b.indexOf('bad', 'enc'), TypeError)
+
+ // test hex encoding
+ t.equal(
+ Buffer.from(b.toString('hex'), 'hex')
+ .indexOf('64', 0, 'hex'),
+ 3
+ )
+ t.equal(
+ Buffer.from(b.toString('hex'), 'hex')
+ .indexOf(Buffer.from('64', 'hex'), 0, 'hex'),
+ 3
+ )
+
+ // test base64 encoding
+ t.equal(
+ Buffer.from(b.toString('base64'), 'base64')
+ .indexOf('ZA==', 0, 'base64'),
+ 3
+ )
+ t.equal(
+ Buffer.from(b.toString('base64'), 'base64')
+ .indexOf(Buffer.from('ZA==', 'base64'), 0, 'base64'),
+ 3
+ )
+
+ // test ascii encoding
+ t.equal(
+ Buffer.from(b.toString('ascii'), 'ascii')
+ .indexOf('d', 0, 'ascii'),
+ 3
+ )
+ t.equal(
+ Buffer.from(b.toString('ascii'), 'ascii')
+ .indexOf(Buffer.from('d', 'ascii'), 0, 'ascii'),
+ 3
+ )
+
+ // test optional offset with passed encoding
+ t.equal(Buffer.from('aaaa0').indexOf('30', 'hex'), 4)
+ t.equal(Buffer.from('aaaa00a').indexOf('3030', 'hex'), 4)
+
+ {
+ // test usc2 encoding
+ const twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2')
+
+ t.equal(8, twoByteString.indexOf('\u0395', 4, 'ucs2'))
+ t.equal(6, twoByteString.indexOf('\u03a3', -4, 'ucs2'))
+ t.equal(4, twoByteString.indexOf('\u03a3', -6, 'ucs2'))
+ t.equal(4, twoByteString.indexOf(
+ Buffer.from('\u03a3', 'ucs2'), -6, 'ucs2'))
+ t.equal(-1, twoByteString.indexOf('\u03a3', -2, 'ucs2'))
+ }
+
+ const mixedByteStringUcs2 =
+ Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2')
+
+ t.equal(6, mixedByteStringUcs2.indexOf('bc', 0, 'ucs2'))
+ t.equal(10, mixedByteStringUcs2.indexOf('\u03a3', 0, 'ucs2'))
+ t.equal(-1, mixedByteStringUcs2.indexOf('\u0396', 0, 'ucs2'))
+
+ t.equal(
+ 6, mixedByteStringUcs2.indexOf(Buffer.from('bc', 'ucs2'), 0, 'ucs2'))
+ t.equal(
+ 10, mixedByteStringUcs2.indexOf(Buffer.from('\u03a3', 'ucs2'), 0, 'ucs2'))
+ t.equal(
+ -1, mixedByteStringUcs2.indexOf(Buffer.from('\u0396', 'ucs2'), 0, 'ucs2'))
+
+ {
+ const twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2')
+
+ // Test single char pattern
+ t.equal(0, twoByteString.indexOf('\u039a', 0, 'ucs2'))
+ let index = twoByteString.indexOf('\u0391', 0, 'ucs2')
+ t.equal(2, index, `Alpha - at index ${index}`)
+ index = twoByteString.indexOf('\u03a3', 0, 'ucs2')
+ t.equal(4, index, `First Sigma - at index ${index}`)
+ index = twoByteString.indexOf('\u03a3', 6, 'ucs2')
+ t.equal(6, index, `Second Sigma - at index ${index}`)
+ index = twoByteString.indexOf('\u0395', 0, 'ucs2')
+ t.equal(8, index, `Epsilon - at index ${index}`)
+ index = twoByteString.indexOf('\u0392', 0, 'ucs2')
+ t.equal(-1, index, `Not beta - at index ${index}`)
+
+ // Test multi-char pattern
+ index = twoByteString.indexOf('\u039a\u0391', 0, 'ucs2')
+ t.equal(0, index, `Lambda Alpha - at index ${index}`)
+ index = twoByteString.indexOf('\u0391\u03a3', 0, 'ucs2')
+ t.equal(2, index, `Alpha Sigma - at index ${index}`)
+ index = twoByteString.indexOf('\u03a3\u03a3', 0, 'ucs2')
+ t.equal(4, index, `Sigma Sigma - at index ${index}`)
+ index = twoByteString.indexOf('\u03a3\u0395', 0, 'ucs2')
+ t.equal(6, index, `Sigma Epsilon - at index ${index}`)
+ }
+
+ const mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395')
+
+ t.equal(5, mixedByteStringUtf8.indexOf('bc'))
+ t.equal(5, mixedByteStringUtf8.indexOf('bc', 5))
+ t.equal(5, mixedByteStringUtf8.indexOf('bc', -8))
+ t.equal(7, mixedByteStringUtf8.indexOf('\u03a3'))
+ t.equal(-1, mixedByteStringUtf8.indexOf('\u0396'))
+
+ // Test complex string indexOf algorithms. Only trigger for long strings.
+ // Long string that isn't a simple repeat of a shorter string.
+ let longString = 'A'
+ for (let i = 66; i < 76; i++) { // from 'B' to 'K'
+ longString = longString + String.fromCharCode(i) + longString
+ }
+
+ const longBufferString = Buffer.from(longString)
+
+ // pattern of 15 chars, repeated every 16 chars in long
+ let pattern = 'ABACABADABACABA'
+ for (let i = 0; i < longBufferString.length - pattern.length; i += 7) {
+ const index = longBufferString.indexOf(pattern, i)
+ t.equal((i + 15) & ~0xf, index,
+ `Long ABACABA...-string at index ${i}`)
+ }
+
+ let index = longBufferString.indexOf('AJABACA')
+ t.equal(510, index, `Long AJABACA, First J - at index ${index}`)
+ index = longBufferString.indexOf('AJABACA', 511)
+ t.equal(1534, index, `Long AJABACA, Second J - at index ${index}`)
+
+ pattern = 'JABACABADABACABA'
+ index = longBufferString.indexOf(pattern)
+ t.equal(511, index, `Long JABACABA..., First J - at index ${index}`)
+ index = longBufferString.indexOf(pattern, 512)
+ t.equal(
+ 1535, index, `Long JABACABA..., Second J - at index ${index}`)
+
+ // Search for a non-ASCII string in a pure ASCII string.
+ const asciiString = Buffer.from(
+ 'somethingnotatallsinisterwhichalsoworks')
+ t.equal(-1, asciiString.indexOf('\x2061'))
+ t.equal(3, asciiString.indexOf('eth', 0))
+
+ // Search in string containing many non-ASCII chars.
+ const allCodePoints = []
+ for (let i = 0; i < 65536; i++) {
+ allCodePoints[i] = i
+ }
+
+ const allCharsString = String.fromCharCode.apply(String, allCodePoints)
+ const allCharsBufferUtf8 = Buffer.from(allCharsString)
+ const allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2')
+
+ // Search for string long enough to trigger complex search with ASCII pattern
+ // and UC16 subject.
+ t.equal(-1, allCharsBufferUtf8.indexOf('notfound'))
+ t.equal(-1, allCharsBufferUcs2.indexOf('notfound'))
+
+ // Needle is longer than haystack, but only because it's encoded as UTF-16
+ t.equal(Buffer.from('aaaa').indexOf('a'.repeat(4), 'ucs2'), -1)
+
+ t.equal(Buffer.from('aaaa').indexOf('a'.repeat(4), 'utf8'), 0)
+ t.equal(Buffer.from('aaaa').indexOf('你好', 'ucs2'), -1)
+
+ // Haystack has odd length, but the needle is UCS2.
+ t.equal(Buffer.from('aaaaa').indexOf('b', 'ucs2'), -1)
+
+ {
+ // Find substrings in Utf8.
+ const lengths = [1, 3, 15] // Single char, simple and complex.
+ const indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b]
+ for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
+ for (let i = 0; i < indices.length; i++) {
+ const index = indices[i]
+ let length = lengths[lengthIndex]
+
+ if (index + length > 0x7F) {
+ length = 2 * length
+ }
+
+ if (index + length > 0x7FF) {
+ length = 3 * length
+ }
+
+ if (index + length > 0xFFFF) {
+ length = 4 * length
+ }
+
+ const patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length)
+ t.equal(index, allCharsBufferUtf8.indexOf(patternBufferUtf8))
+
+ const patternStringUtf8 = patternBufferUtf8.toString()
+ t.equal(index, allCharsBufferUtf8.indexOf(patternStringUtf8))
+ }
+ }
+ }
+
+ {
+ // Find substrings in Usc2.
+ const lengths = [2, 4, 16] // Single char, simple and complex.
+ const indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0]
+
+ for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
+ for (let i = 0; i < indices.length; i++) {
+ const index = indices[i] * 2
+ const length = lengths[lengthIndex]
+
+ const patternBufferUcs2 =
+ allCharsBufferUcs2.slice(index, index + length)
+ t.equal(
+ index, allCharsBufferUcs2.indexOf(patternBufferUcs2, 0, 'ucs2'))
+
+ const patternStringUcs2 = patternBufferUcs2.toString('ucs2')
+ t.equal(
+ index, allCharsBufferUcs2.indexOf(patternStringUcs2, 0, 'ucs2'))
+ }
+ }
+ }
+
+ [
+ () => {},
+ {},
+ []
+ ].forEach((val) => {
+ t.throws(() => b.indexOf(val), TypeError, `"${JSON.stringify(val)}" should throw`)
+ })
+
+ // Test weird offset arguments.
+ // The following offsets coerce to NaN or 0, searching the whole Buffer
+ t.equal(b.indexOf('b', undefined), 1)
+ t.equal(b.indexOf('b', {}), 1)
+ t.equal(b.indexOf('b', 0), 1)
+ t.equal(b.indexOf('b', null), 1)
+ t.equal(b.indexOf('b', []), 1)
+
+ // The following offset coerces to 2, in other words +[2] === 2
+ t.equal(b.indexOf('b', [2]), -1)
+
+ // Behavior should match String.indexOf()
+ t.equal(
+ b.indexOf('b', undefined),
+ stringComparison.indexOf('b', undefined))
+ t.equal(
+ b.indexOf('b', {}),
+ stringComparison.indexOf('b', {}))
+ t.equal(
+ b.indexOf('b', 0),
+ stringComparison.indexOf('b', 0))
+ t.equal(
+ b.indexOf('b', null),
+ stringComparison.indexOf('b', null))
+ t.equal(
+ b.indexOf('b', []),
+ stringComparison.indexOf('b', []))
+ t.equal(
+ b.indexOf('b', [2]),
+ stringComparison.indexOf('b', [2]))
+
+ // test truncation of Number arguments to uint8
+ {
+ const buf = Buffer.from('this is a test')
+
+ t.equal(buf.indexOf(0x6973), 3)
+ t.equal(buf.indexOf(0x697320), 4)
+ t.equal(buf.indexOf(0x69732069), 2)
+ t.equal(buf.indexOf(0x697374657374), 0)
+ t.equal(buf.indexOf(0x69737374), 0)
+ t.equal(buf.indexOf(0x69737465), 11)
+ t.equal(buf.indexOf(0x69737465), 11)
+ t.equal(buf.indexOf(-140), 0)
+ t.equal(buf.indexOf(-152), 1)
+ t.equal(buf.indexOf(0xff), -1)
+ t.equal(buf.indexOf(0xffff), -1)
+ }
+
+ // Test that Uint8Array arguments are okay.
+ {
+ const needle = new Uint8Array([0x66, 0x6f, 0x6f])
+ const haystack = new BufferList(Buffer.from('a foo b foo'))
+ t.equal(haystack.indexOf(needle), 2)
+ }
+
+ t.end()
+})
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/isBufferList.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/isBufferList.js
new file mode 100644
index 000000000..9d895d59b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/isBufferList.js
@@ -0,0 +1,32 @@
+'use strict'
+
+const tape = require('tape')
+const { BufferList, BufferListStream } = require('../')
+const { Buffer } = require('buffer')
+
+tape('isBufferList positives', (t) => {
+ t.ok(BufferList.isBufferList(new BufferList()))
+ t.ok(BufferList.isBufferList(new BufferListStream()))
+
+ t.end()
+})
+
+tape('isBufferList negatives', (t) => {
+ const types = [
+ null,
+ undefined,
+ NaN,
+ true,
+ false,
+ {},
+ [],
+ Buffer.alloc(0),
+ [Buffer.alloc(0)]
+ ]
+
+ for (const obj of types) {
+ t.notOk(BufferList.isBufferList(obj))
+ }
+
+ t.end()
+})
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/test.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/test.js
new file mode 100644
index 000000000..e523d0c3f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bl/test/test.js
@@ -0,0 +1,869 @@
+'use strict'
+
+const tape = require('tape')
+const crypto = require('crypto')
+const fs = require('fs')
+const path = require('path')
+const BufferList = require('../')
+const { Buffer } = require('buffer')
+
+const encodings =
+ ('hex utf8 utf-8 ascii binary base64' +
+ (process.browser ? '' : ' ucs2 ucs-2 utf16le utf-16le')).split(' ')
+
+require('./indexOf')
+require('./isBufferList')
+require('./convert')
+
+tape('single bytes from single buffer', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abcd'))
+
+ t.equal(bl.length, 4)
+ t.equal(bl.get(-1), undefined)
+ t.equal(bl.get(0), 97)
+ t.equal(bl.get(1), 98)
+ t.equal(bl.get(2), 99)
+ t.equal(bl.get(3), 100)
+ t.equal(bl.get(4), undefined)
+
+ t.end()
+})
+
+tape('single bytes from multiple buffers', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abcd'))
+ bl.append(Buffer.from('efg'))
+ bl.append(Buffer.from('hi'))
+ bl.append(Buffer.from('j'))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.get(0), 97)
+ t.equal(bl.get(1), 98)
+ t.equal(bl.get(2), 99)
+ t.equal(bl.get(3), 100)
+ t.equal(bl.get(4), 101)
+ t.equal(bl.get(5), 102)
+ t.equal(bl.get(6), 103)
+ t.equal(bl.get(7), 104)
+ t.equal(bl.get(8), 105)
+ t.equal(bl.get(9), 106)
+
+ t.end()
+})
+
+tape('multi bytes from single buffer', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abcd'))
+
+ t.equal(bl.length, 4)
+
+ t.equal(bl.slice(0, 4).toString('ascii'), 'abcd')
+ t.equal(bl.slice(0, 3).toString('ascii'), 'abc')
+ t.equal(bl.slice(1, 4).toString('ascii'), 'bcd')
+ t.equal(bl.slice(-4, -1).toString('ascii'), 'abc')
+
+ t.end()
+})
+
+tape('multi bytes from single buffer (negative indexes)', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('buffer'))
+
+ t.equal(bl.length, 6)
+
+ t.equal(bl.slice(-6, -1).toString('ascii'), 'buffe')
+ t.equal(bl.slice(-6, -2).toString('ascii'), 'buff')
+ t.equal(bl.slice(-5, -2).toString('ascii'), 'uff')
+
+ t.end()
+})
+
+tape('multiple bytes from multiple buffers', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abcd'))
+ bl.append(Buffer.from('efg'))
+ bl.append(Buffer.from('hi'))
+ bl.append(Buffer.from('j'))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
+ t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
+ t.equal(bl.slice(3, 6).toString('ascii'), 'def')
+ t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
+ t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
+ t.equal(bl.slice(-7, -4).toString('ascii'), 'def')
+
+ t.end()
+})
+
+tape('multiple bytes from multiple buffer lists', function (t) {
+ const bl = new BufferList()
+
+ bl.append(new BufferList([Buffer.from('abcd'), Buffer.from('efg')]))
+ bl.append(new BufferList([Buffer.from('hi'), Buffer.from('j')]))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
+
+ t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
+ t.equal(bl.slice(3, 6).toString('ascii'), 'def')
+ t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
+ t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
+
+ t.end()
+})
+
+// same data as previous test, just using nested constructors
+tape('multiple bytes from crazy nested buffer lists', function (t) {
+ const bl = new BufferList()
+
+ bl.append(new BufferList([
+ new BufferList([
+ new BufferList(Buffer.from('abc')),
+ Buffer.from('d'),
+ new BufferList(Buffer.from('efg'))
+ ]),
+ new BufferList([Buffer.from('hi')]),
+ new BufferList(Buffer.from('j'))
+ ]))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
+
+ t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
+ t.equal(bl.slice(3, 6).toString('ascii'), 'def')
+ t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
+ t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
+
+ t.end()
+})
+
+tape('append accepts arrays of Buffers', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abc'))
+ bl.append([Buffer.from('def')])
+ bl.append([Buffer.from('ghi'), Buffer.from('jkl')])
+ bl.append([Buffer.from('mnop'), Buffer.from('qrstu'), Buffer.from('vwxyz')])
+ t.equal(bl.length, 26)
+ t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
+
+ t.end()
+})
+
+tape('append accepts arrays of Uint8Arrays', function (t) {
+ const bl = new BufferList()
+
+ bl.append(new Uint8Array([97, 98, 99]))
+ bl.append([Uint8Array.from([100, 101, 102])])
+ bl.append([new Uint8Array([103, 104, 105]), new Uint8Array([106, 107, 108])])
+ bl.append([new Uint8Array([109, 110, 111, 112]), new Uint8Array([113, 114, 115, 116, 117]), new Uint8Array([118, 119, 120, 121, 122])])
+ t.equal(bl.length, 26)
+ t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
+
+ t.end()
+})
+
+tape('append accepts arrays of BufferLists', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abc'))
+ bl.append([new BufferList('def')])
+ bl.append(new BufferList([Buffer.from('ghi'), new BufferList('jkl')]))
+ bl.append([Buffer.from('mnop'), new BufferList([Buffer.from('qrstu'), Buffer.from('vwxyz')])])
+ t.equal(bl.length, 26)
+ t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
+
+ t.end()
+})
+
+tape('append chainable', function (t) {
+ const bl = new BufferList()
+
+ t.ok(bl.append(Buffer.from('abcd')) === bl)
+ t.ok(bl.append([Buffer.from('abcd')]) === bl)
+ t.ok(bl.append(new BufferList(Buffer.from('abcd'))) === bl)
+ t.ok(bl.append([new BufferList(Buffer.from('abcd'))]) === bl)
+
+ t.end()
+})
+
+tape('append chainable (test results)', function (t) {
+ const bl = new BufferList('abc')
+ .append([new BufferList('def')])
+ .append(new BufferList([Buffer.from('ghi'), new BufferList('jkl')]))
+ .append([Buffer.from('mnop'), new BufferList([Buffer.from('qrstu'), Buffer.from('vwxyz')])])
+
+ t.equal(bl.length, 26)
+ t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
+
+ t.end()
+})
+
+tape('consuming from multiple buffers', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abcd'))
+ bl.append(Buffer.from('efg'))
+ bl.append(Buffer.from('hi'))
+ bl.append(Buffer.from('j'))
+
+ t.equal(bl.length, 10)
+
+ t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
+
+ bl.consume(3)
+ t.equal(bl.length, 7)
+ t.equal(bl.slice(0, 7).toString('ascii'), 'defghij')
+
+ bl.consume(2)
+ t.equal(bl.length, 5)
+ t.equal(bl.slice(0, 5).toString('ascii'), 'fghij')
+
+ bl.consume(1)
+ t.equal(bl.length, 4)
+ t.equal(bl.slice(0, 4).toString('ascii'), 'ghij')
+
+ bl.consume(1)
+ t.equal(bl.length, 3)
+ t.equal(bl.slice(0, 3).toString('ascii'), 'hij')
+
+ bl.consume(2)
+ t.equal(bl.length, 1)
+ t.equal(bl.slice(0, 1).toString('ascii'), 'j')
+
+ t.end()
+})
+
+tape('complete consumption', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('a'))
+ bl.append(Buffer.from('b'))
+
+ bl.consume(2)
+
+ t.equal(bl.length, 0)
+ t.equal(bl._bufs.length, 0)
+
+ t.end()
+})
+
+tape('test readUInt8 / readInt8', function (t) {
+ const buf1 = Buffer.alloc(1)
+ const buf2 = Buffer.alloc(3)
+ const buf3 = Buffer.alloc(3)
+ const bl = new BufferList()
+
+ buf1[0] = 0x1
+ buf2[1] = 0x3
+ buf2[2] = 0x4
+ buf3[0] = 0x23
+ buf3[1] = 0x42
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readUInt8(), 0x1)
+ t.equal(bl.readUInt8(2), 0x3)
+ t.equal(bl.readInt8(2), 0x3)
+ t.equal(bl.readUInt8(3), 0x4)
+ t.equal(bl.readInt8(3), 0x4)
+ t.equal(bl.readUInt8(4), 0x23)
+ t.equal(bl.readInt8(4), 0x23)
+ t.equal(bl.readUInt8(5), 0x42)
+ t.equal(bl.readInt8(5), 0x42)
+
+ t.end()
+})
+
+tape('test readUInt16LE / readUInt16BE / readInt16LE / readInt16BE', function (t) {
+ const buf1 = Buffer.alloc(1)
+ const buf2 = Buffer.alloc(3)
+ const buf3 = Buffer.alloc(3)
+ const bl = new BufferList()
+
+ buf1[0] = 0x1
+ buf2[1] = 0x3
+ buf2[2] = 0x4
+ buf3[0] = 0x23
+ buf3[1] = 0x42
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readUInt16BE(), 0x0100)
+ t.equal(bl.readUInt16LE(), 0x0001)
+ t.equal(bl.readUInt16BE(2), 0x0304)
+ t.equal(bl.readUInt16LE(2), 0x0403)
+ t.equal(bl.readInt16BE(2), 0x0304)
+ t.equal(bl.readInt16LE(2), 0x0403)
+ t.equal(bl.readUInt16BE(3), 0x0423)
+ t.equal(bl.readUInt16LE(3), 0x2304)
+ t.equal(bl.readInt16BE(3), 0x0423)
+ t.equal(bl.readInt16LE(3), 0x2304)
+ t.equal(bl.readUInt16BE(4), 0x2342)
+ t.equal(bl.readUInt16LE(4), 0x4223)
+ t.equal(bl.readInt16BE(4), 0x2342)
+ t.equal(bl.readInt16LE(4), 0x4223)
+
+ t.end()
+})
+
+tape('test readUInt32LE / readUInt32BE / readInt32LE / readInt32BE', function (t) {
+ const buf1 = Buffer.alloc(1)
+ const buf2 = Buffer.alloc(3)
+ const buf3 = Buffer.alloc(3)
+ const bl = new BufferList()
+
+ buf1[0] = 0x1
+ buf2[1] = 0x3
+ buf2[2] = 0x4
+ buf3[0] = 0x23
+ buf3[1] = 0x42
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readUInt32BE(), 0x01000304)
+ t.equal(bl.readUInt32LE(), 0x04030001)
+ t.equal(bl.readUInt32BE(2), 0x03042342)
+ t.equal(bl.readUInt32LE(2), 0x42230403)
+ t.equal(bl.readInt32BE(2), 0x03042342)
+ t.equal(bl.readInt32LE(2), 0x42230403)
+
+ t.end()
+})
+
+tape('test readUIntLE / readUIntBE / readIntLE / readIntBE', function (t) {
+ const buf1 = Buffer.alloc(1)
+ const buf2 = Buffer.alloc(3)
+ const buf3 = Buffer.alloc(3)
+ const bl = new BufferList()
+
+ buf2[0] = 0x2
+ buf2[1] = 0x3
+ buf2[2] = 0x4
+ buf3[0] = 0x23
+ buf3[1] = 0x42
+ buf3[2] = 0x61
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ t.equal(bl.readUIntBE(1, 1), 0x02)
+ t.equal(bl.readUIntBE(1, 2), 0x0203)
+ t.equal(bl.readUIntBE(1, 3), 0x020304)
+ t.equal(bl.readUIntBE(1, 4), 0x02030423)
+ t.equal(bl.readUIntBE(1, 5), 0x0203042342)
+ t.equal(bl.readUIntBE(1, 6), 0x020304234261)
+ t.equal(bl.readUIntLE(1, 1), 0x02)
+ t.equal(bl.readUIntLE(1, 2), 0x0302)
+ t.equal(bl.readUIntLE(1, 3), 0x040302)
+ t.equal(bl.readUIntLE(1, 4), 0x23040302)
+ t.equal(bl.readUIntLE(1, 5), 0x4223040302)
+ t.equal(bl.readUIntLE(1, 6), 0x614223040302)
+ t.equal(bl.readIntBE(1, 1), 0x02)
+ t.equal(bl.readIntBE(1, 2), 0x0203)
+ t.equal(bl.readIntBE(1, 3), 0x020304)
+ t.equal(bl.readIntBE(1, 4), 0x02030423)
+ t.equal(bl.readIntBE(1, 5), 0x0203042342)
+ t.equal(bl.readIntBE(1, 6), 0x020304234261)
+ t.equal(bl.readIntLE(1, 1), 0x02)
+ t.equal(bl.readIntLE(1, 2), 0x0302)
+ t.equal(bl.readIntLE(1, 3), 0x040302)
+ t.equal(bl.readIntLE(1, 4), 0x23040302)
+ t.equal(bl.readIntLE(1, 5), 0x4223040302)
+ t.equal(bl.readIntLE(1, 6), 0x614223040302)
+
+ t.end()
+})
+
+tape('test readFloatLE / readFloatBE', function (t) {
+ const buf1 = Buffer.alloc(1)
+ const buf2 = Buffer.alloc(3)
+ const buf3 = Buffer.alloc(3)
+ const bl = new BufferList()
+
+ buf1[0] = 0x01
+ buf2[1] = 0x00
+ buf2[2] = 0x00
+ buf3[0] = 0x80
+ buf3[1] = 0x3f
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ const canonical = Buffer.concat([buf1, buf2, buf3])
+ t.equal(bl.readFloatLE(), canonical.readFloatLE())
+ t.equal(bl.readFloatBE(), canonical.readFloatBE())
+ t.equal(bl.readFloatLE(2), canonical.readFloatLE(2))
+ t.equal(bl.readFloatBE(2), canonical.readFloatBE(2))
+
+ t.end()
+})
+
+tape('test readDoubleLE / readDoubleBE', function (t) {
+ const buf1 = Buffer.alloc(1)
+ const buf2 = Buffer.alloc(3)
+ const buf3 = Buffer.alloc(10)
+ const bl = new BufferList()
+
+ buf1[0] = 0x01
+ buf2[1] = 0x55
+ buf2[2] = 0x55
+ buf3[0] = 0x55
+ buf3[1] = 0x55
+ buf3[2] = 0x55
+ buf3[3] = 0x55
+ buf3[4] = 0xd5
+ buf3[5] = 0x3f
+
+ bl.append(buf1)
+ bl.append(buf2)
+ bl.append(buf3)
+
+ const canonical = Buffer.concat([buf1, buf2, buf3])
+ t.equal(bl.readDoubleBE(), canonical.readDoubleBE())
+ t.equal(bl.readDoubleLE(), canonical.readDoubleLE())
+ t.equal(bl.readDoubleBE(2), canonical.readDoubleBE(2))
+ t.equal(bl.readDoubleLE(2), canonical.readDoubleLE(2))
+
+ t.end()
+})
+
+tape('test toString', function (t) {
+ const bl = new BufferList()
+
+ bl.append(Buffer.from('abcd'))
+ bl.append(Buffer.from('efg'))
+ bl.append(Buffer.from('hi'))
+ bl.append(Buffer.from('j'))
+
+ t.equal(bl.toString('ascii', 0, 10), 'abcdefghij')
+ t.equal(bl.toString('ascii', 3, 10), 'defghij')
+ t.equal(bl.toString('ascii', 3, 6), 'def')
+ t.equal(bl.toString('ascii', 3, 8), 'defgh')
+ t.equal(bl.toString('ascii', 5, 10), 'fghij')
+
+ t.end()
+})
+
+tape('test toString encoding', function (t) {
+ const bl = new BufferList()
+ const b = Buffer.from('abcdefghij\xff\x00')
+
+ bl.append(Buffer.from('abcd'))
+ bl.append(Buffer.from('efg'))
+ bl.append(Buffer.from('hi'))
+ bl.append(Buffer.from('j'))
+ bl.append(Buffer.from('\xff\x00'))
+
+ encodings.forEach(function (enc) {
+ t.equal(bl.toString(enc), b.toString(enc), enc)
+ })
+
+ t.end()
+})
+
+tape('uninitialized memory', function (t) {
+ const secret = crypto.randomBytes(256)
+ for (let i = 0; i < 1e6; i++) {
+ const clone = Buffer.from(secret)
+ const bl = new BufferList()
+ bl.append(Buffer.from('a'))
+ bl.consume(-1024)
+ const buf = bl.slice(1)
+ if (buf.indexOf(clone) !== -1) {
+ t.fail(`Match (at ${i})`)
+ break
+ }
+ }
+ t.end()
+})
+
+!process.browser && tape('test stream', function (t) {
+ const random = crypto.randomBytes(65534)
+
+ const bl = new BufferList((err, buf) => {
+ t.ok(Buffer.isBuffer(buf))
+ t.ok(err === null)
+ t.ok(random.equals(bl.slice()))
+ t.ok(random.equals(buf.slice()))
+
+ bl.pipe(fs.createWriteStream('/tmp/bl_test_rnd_out.dat'))
+ .on('close', function () {
+ const rndhash = crypto.createHash('md5').update(random).digest('hex')
+ const md5sum = crypto.createHash('md5')
+ const s = fs.createReadStream('/tmp/bl_test_rnd_out.dat')
+
+ s.on('data', md5sum.update.bind(md5sum))
+ s.on('end', function () {
+ t.equal(rndhash, md5sum.digest('hex'), 'woohoo! correct hash!')
+ t.end()
+ })
+ })
+ })
+
+ fs.writeFileSync('/tmp/bl_test_rnd.dat', random)
+ fs.createReadStream('/tmp/bl_test_rnd.dat').pipe(bl)
+})
+
+tape('instantiation with Buffer', function (t) {
+ const buf = crypto.randomBytes(1024)
+ const buf2 = crypto.randomBytes(1024)
+ let b = BufferList(buf)
+
+ t.equal(buf.toString('hex'), b.slice().toString('hex'), 'same buffer')
+ b = BufferList([buf, buf2])
+ t.equal(b.slice().toString('hex'), Buffer.concat([buf, buf2]).toString('hex'), 'same buffer')
+
+ t.end()
+})
+
+tape('test String appendage', function (t) {
+ const bl = new BufferList()
+ const b = Buffer.from('abcdefghij\xff\x00')
+
+ bl.append('abcd')
+ bl.append('efg')
+ bl.append('hi')
+ bl.append('j')
+ bl.append('\xff\x00')
+
+ encodings.forEach(function (enc) {
+ t.equal(bl.toString(enc), b.toString(enc))
+ })
+
+ t.end()
+})
+
+tape('test Number appendage', function (t) {
+ const bl = new BufferList()
+ const b = Buffer.from('1234567890')
+
+ bl.append(1234)
+ bl.append(567)
+ bl.append(89)
+ bl.append(0)
+
+ encodings.forEach(function (enc) {
+ t.equal(bl.toString(enc), b.toString(enc))
+ })
+
+ t.end()
+})
+
+tape('write nothing, should get empty buffer', function (t) {
+ t.plan(3)
+ BufferList(function (err, data) {
+ t.notOk(err, 'no error')
+ t.ok(Buffer.isBuffer(data), 'got a buffer')
+ t.equal(0, data.length, 'got a zero-length buffer')
+ t.end()
+ }).end()
+})
+
+tape('unicode string', function (t) {
+ t.plan(2)
+
+ const inp1 = '\u2600'
+ const inp2 = '\u2603'
+ const exp = inp1 + ' and ' + inp2
+ const bl = BufferList()
+
+ bl.write(inp1)
+ bl.write(' and ')
+ bl.write(inp2)
+ t.equal(exp, bl.toString())
+ t.equal(Buffer.from(exp).toString('hex'), bl.toString('hex'))
+})
+
+tape('should emit finish', function (t) {
+ const source = BufferList()
+ const dest = BufferList()
+
+ source.write('hello')
+ source.pipe(dest)
+
+ dest.on('finish', function () {
+ t.equal(dest.toString('utf8'), 'hello')
+ t.end()
+ })
+})
+
+tape('basic copy', function (t) {
+ const buf = crypto.randomBytes(1024)
+ const buf2 = Buffer.alloc(1024)
+ const b = BufferList(buf)
+
+ b.copy(buf2)
+ t.equal(b.slice().toString('hex'), buf2.toString('hex'), 'same buffer')
+
+ t.end()
+})
+
+tape('copy after many appends', function (t) {
+ const buf = crypto.randomBytes(512)
+ const buf2 = Buffer.alloc(1024)
+ const b = BufferList(buf)
+
+ b.append(buf)
+ b.copy(buf2)
+ t.equal(b.slice().toString('hex'), buf2.toString('hex'), 'same buffer')
+
+ t.end()
+})
+
+tape('copy at a precise position', function (t) {
+ const buf = crypto.randomBytes(1004)
+ const buf2 = Buffer.alloc(1024)
+ const b = BufferList(buf)
+
+ b.copy(buf2, 20)
+ t.equal(b.slice().toString('hex'), buf2.slice(20).toString('hex'), 'same buffer')
+
+ t.end()
+})
+
+tape('copy starting from a precise location', function (t) {
+ const buf = crypto.randomBytes(10)
+ const buf2 = Buffer.alloc(5)
+ const b = BufferList(buf)
+
+ b.copy(buf2, 0, 5)
+ t.equal(b.slice(5).toString('hex'), buf2.toString('hex'), 'same buffer')
+
+ t.end()
+})
+
+tape('copy in an interval', function (t) {
+ const rnd = crypto.randomBytes(10)
+ const b = BufferList(rnd) // put the random bytes there
+ const actual = Buffer.alloc(3)
+ const expected = Buffer.alloc(3)
+
+ rnd.copy(expected, 0, 5, 8)
+ b.copy(actual, 0, 5, 8)
+
+ t.equal(actual.toString('hex'), expected.toString('hex'), 'same buffer')
+
+ t.end()
+})
+
+tape('copy an interval between two buffers', function (t) {
+ const buf = crypto.randomBytes(10)
+ const buf2 = Buffer.alloc(10)
+ const b = BufferList(buf)
+
+ b.append(buf)
+ b.copy(buf2, 0, 5, 15)
+
+ t.equal(b.slice(5, 15).toString('hex'), buf2.toString('hex'), 'same buffer')
+
+ t.end()
+})
+
+tape('shallow slice across buffer boundaries', function (t) {
+ const bl = new BufferList(['First', 'Second', 'Third'])
+
+ t.equal(bl.shallowSlice(3, 13).toString(), 'stSecondTh')
+
+ t.end()
+})
+
+tape('shallow slice within single buffer', function (t) {
+ t.plan(2)
+
+ const bl = new BufferList(['First', 'Second', 'Third'])
+
+ t.equal(bl.shallowSlice(5, 10).toString(), 'Secon')
+ t.equal(bl.shallowSlice(7, 10).toString(), 'con')
+
+ t.end()
+})
+
+tape('shallow slice single buffer', function (t) {
+ t.plan(3)
+
+ const bl = new BufferList(['First', 'Second', 'Third'])
+
+ t.equal(bl.shallowSlice(0, 5).toString(), 'First')
+ t.equal(bl.shallowSlice(5, 11).toString(), 'Second')
+ t.equal(bl.shallowSlice(11, 16).toString(), 'Third')
+})
+
+tape('shallow slice with negative or omitted indices', function (t) {
+ t.plan(4)
+
+ const bl = new BufferList(['First', 'Second', 'Third'])
+
+ t.equal(bl.shallowSlice().toString(), 'FirstSecondThird')
+ t.equal(bl.shallowSlice(5).toString(), 'SecondThird')
+ t.equal(bl.shallowSlice(5, -3).toString(), 'SecondTh')
+ t.equal(bl.shallowSlice(-8).toString(), 'ondThird')
+})
+
+tape('shallow slice does not make a copy', function (t) {
+ t.plan(1)
+
+ const buffers = [Buffer.from('First'), Buffer.from('Second'), Buffer.from('Third')]
+ const bl = (new BufferList(buffers)).shallowSlice(5, -3)
+
+ buffers[1].fill('h')
+ buffers[2].fill('h')
+
+ t.equal(bl.toString(), 'hhhhhhhh')
+})
+
+tape('shallow slice with 0 length', function (t) {
+ t.plan(1)
+
+ const buffers = [Buffer.from('First'), Buffer.from('Second'), Buffer.from('Third')]
+ const bl = (new BufferList(buffers)).shallowSlice(0, 0)
+
+ t.equal(bl.length, 0)
+})
+
+tape('shallow slice with 0 length from middle', function (t) {
+ t.plan(1)
+
+ const buffers = [Buffer.from('First'), Buffer.from('Second'), Buffer.from('Third')]
+ const bl = (new BufferList(buffers)).shallowSlice(10, 10)
+
+ t.equal(bl.length, 0)
+})
+
+tape('duplicate', function (t) {
+ t.plan(2)
+
+ const bl = new BufferList('abcdefghij\xff\x00')
+ const dup = bl.duplicate()
+
+ t.equal(bl.prototype, dup.prototype)
+ t.equal(bl.toString('hex'), dup.toString('hex'))
+})
+
+tape('destroy no pipe', function (t) {
+ t.plan(2)
+
+ const bl = new BufferList('alsdkfja;lsdkfja;lsdk')
+
+ bl.destroy()
+
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+})
+
+tape('destroy with error', function (t) {
+ t.plan(3)
+
+ const bl = new BufferList('alsdkfja;lsdkfja;lsdk')
+ const err = new Error('kaboom')
+
+ bl.destroy(err)
+ bl.on('error', function (_err) {
+ t.equal(_err, err)
+ })
+
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+})
+
+!process.browser && tape('destroy with pipe before read end', function (t) {
+ t.plan(2)
+
+ const bl = new BufferList()
+ fs.createReadStream(path.join(__dirname, '/test.js'))
+ .pipe(bl)
+
+ bl.destroy()
+
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+})
+
+!process.browser && tape('destroy with pipe before read end with race', function (t) {
+ t.plan(2)
+
+ const bl = new BufferList()
+
+ fs.createReadStream(path.join(__dirname, '/test.js'))
+ .pipe(bl)
+
+ setTimeout(function () {
+ bl.destroy()
+ setTimeout(function () {
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+ }, 500)
+ }, 500)
+})
+
+!process.browser && tape('destroy with pipe after read end', function (t) {
+ t.plan(2)
+
+ const bl = new BufferList()
+
+ fs.createReadStream(path.join(__dirname, '/test.js'))
+ .on('end', onEnd)
+ .pipe(bl)
+
+ function onEnd () {
+ bl.destroy()
+
+ t.equal(bl._bufs.length, 0)
+ t.equal(bl.length, 0)
+ }
+})
+
+!process.browser && tape('destroy with pipe while writing to a destination', function (t) {
+ t.plan(4)
+
+ const bl = new BufferList()
+ const ds = new BufferList()
+
+ fs.createReadStream(path.join(__dirname, '/test.js'))
+ .on('end', onEnd)
+ .pipe(bl)
+
+ function onEnd () {
+ bl.pipe(ds)
+
+ setTimeout(function () {
+ bl.destroy()
+
+ t.equals(bl._bufs.length, 0)
+ t.equals(bl.length, 0)
+
+ ds.destroy()
+
+ t.equals(bl._bufs.length, 0)
+ t.equals(bl.length, 0)
+ }, 100)
+ }
+})
+
+!process.browser && tape('handle error', function (t) {
+ t.plan(2)
+
+ fs.createReadStream('/does/not/exist').pipe(BufferList(function (err, data) {
+ t.ok(err instanceof Error, 'has error')
+ t.notOk(data, 'no data')
+ }))
+})
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/LICENSE
new file mode 100644
index 000000000..386b7b694
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/LICENSE
@@ -0,0 +1,23 @@
+(The MIT License)
+
+Copyright (c) 2014 Jonathan Ong
+Copyright (c) 2014-2015 Douglas Christopher Wilson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/README.md
new file mode 100644
index 000000000..31b66ce06
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/README.md
@@ -0,0 +1,494 @@
+# body-parser
+
+[![NPM Version][npm-version-image]][npm-url]
+[![NPM Downloads][npm-downloads-image]][npm-url]
+[![Build Status][ci-image]][ci-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
+
+Node.js body parsing middleware.
+
+Parse incoming request bodies in a middleware before your handlers, available
+under the `req.body` property.
+
+**Note** As `req.body`'s shape is based on user-controlled input, all
+properties and values in this object are untrusted and should be validated
+before trusting. For example, `req.body.foo.toString()` may fail in multiple
+ways, for example the `foo` property may not be there or may not be a string,
+and `toString` may not be a function and instead a string or other user input.
+
+[Learn about the anatomy of an HTTP transaction in Node.js](https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/).
+
+_This does not handle multipart bodies_, due to their complex and typically
+large nature. For multipart bodies, you may be interested in the following
+modules:
+
+ * [busboy](https://www.npmjs.org/package/busboy#readme) and
+ [connect-busboy](https://www.npmjs.org/package/connect-busboy#readme)
+ * [multiparty](https://www.npmjs.org/package/multiparty#readme) and
+ [connect-multiparty](https://www.npmjs.org/package/connect-multiparty#readme)
+ * [formidable](https://www.npmjs.org/package/formidable#readme)
+ * [multer](https://www.npmjs.org/package/multer#readme)
+
+This module provides the following parsers:
+
+ * [JSON body parser](#bodyparserjsonoptions)
+ * [Raw body parser](#bodyparserrawoptions)
+ * [Text body parser](#bodyparsertextoptions)
+ * [URL-encoded form body parser](#bodyparserurlencodedoptions)
+
+Other body parsers you might be interested in:
+
+- [body](https://www.npmjs.org/package/body#readme)
+- [co-body](https://www.npmjs.org/package/co-body#readme)
+
+## Installation
+
+```sh
+$ npm install body-parser
+```
+
+## API
+
+```js
+const bodyParser = require('body-parser')
+```
+
+The `bodyParser` object exposes various factories to create middlewares. All
+middlewares will populate the `req.body` property with the parsed body when
+the `Content-Type` request header matches the `type` option.
+
+The various errors returned by this module are described in the
+[errors section](#errors).
+
+### bodyParser.json([options])
+
+Returns middleware that only parses `json` and only looks at requests where
+the `Content-Type` header matches the `type` option. This parser accepts any
+Unicode encoding of the body and supports automatic inflation of `gzip`,
+`br` (brotli) and `deflate` encodings.
+
+A new `body` object containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`).
+
+#### Options
+
+The `json` function takes an optional `options` object that may contain any of
+the following keys:
+
+##### defaultCharset
+
+Specify the default character set for the json content if the charset is not
+specified in the `Content-Type` header of the request. Defaults to `utf-8`.
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### reviver
+
+The `reviver` option is passed directly to `JSON.parse` as the second
+argument. You can find more information on this argument
+[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).
+
+##### strict
+
+When set to `true`, will only accept arrays and objects; when `false` will
+accept anything `JSON.parse` accepts. Defaults to `true`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a string, array of strings, or a function. If not a
+function, `type` option is passed directly to the
+[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
+be an extension name (like `json`), a mime type (like `application/json`), or
+a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type`
+option is called as `fn(req)` and the request is parsed if it returns a truthy
+value. Defaults to `application/json`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+### bodyParser.raw([options])
+
+Returns middleware that parses all bodies as a `Buffer` and only looks at
+requests where the `Content-Type` header matches the `type` option. This
+parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
+encodings.
+
+A new `body` object containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`). This will be a `Buffer` object
+of the body.
+
+#### Options
+
+The `raw` function takes an optional `options` object that may contain any of
+the following keys:
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a string, array of strings, or a function.
+If not a function, `type` option is passed directly to the
+[type-is](https://www.npmjs.org/package/type-is#readme) library and this
+can be an extension name (like `bin`), a mime type (like
+`application/octet-stream`), or a mime type with a wildcard (like `*/*` or
+`application/*`). If a function, the `type` option is called as `fn(req)`
+and the request is parsed if it returns a truthy value. Defaults to
+`application/octet-stream`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+### bodyParser.text([options])
+
+Returns middleware that parses all bodies as a string and only looks at
+requests where the `Content-Type` header matches the `type` option. This
+parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
+encodings.
+
+A new `body` string containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`). This will be a string of the
+body.
+
+#### Options
+
+The `text` function takes an optional `options` object that may contain any of
+the following keys:
+
+##### defaultCharset
+
+Specify the default character set for the text content if the charset is not
+specified in the `Content-Type` header of the request. Defaults to `utf-8`.
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a string, array of strings, or a function. If not
+a function, `type` option is passed directly to the
+[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
+be an extension name (like `txt`), a mime type (like `text/plain`), or a mime
+type with a wildcard (like `*/*` or `text/*`). If a function, the `type`
+option is called as `fn(req)` and the request is parsed if it returns a
+truthy value. Defaults to `text/plain`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+### bodyParser.urlencoded([options])
+
+Returns middleware that only parses `urlencoded` bodies and only looks at
+requests where the `Content-Type` header matches the `type` option. This
+parser accepts only UTF-8 encoding of the body and supports automatic
+inflation of `gzip`, `br` (brotli) and `deflate` encodings.
+
+A new `body` object containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`). This object will contain
+key-value pairs, where the value can be a string or array (when `extended` is
+`false`), or any type (when `extended` is `true`).
+
+#### Options
+
+The `urlencoded` function takes an optional `options` object that may contain
+any of the following keys:
+
+##### extended
+
+The "extended" syntax allows for rich objects and arrays to be encoded into the
+URL-encoded format, allowing for a JSON-like experience with URL-encoded. For
+more information, please [see the qs
+library](https://www.npmjs.org/package/qs#readme).
+
+Defaults to `false`.
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### parameterLimit
+
+The `parameterLimit` option controls the maximum number of parameters that
+are allowed in the URL-encoded data. If a request contains more parameters
+than this value, a 413 will be returned to the client. Defaults to `1000`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a string, array of strings, or a function. If not
+a function, `type` option is passed directly to the
+[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
+be an extension name (like `urlencoded`), a mime type (like
+`application/x-www-form-urlencoded`), or a mime type with a wildcard (like
+`*/x-www-form-urlencoded`). If a function, the `type` option is called as
+`fn(req)` and the request is parsed if it returns a truthy value. Defaults
+to `application/x-www-form-urlencoded`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+##### defaultCharset
+
+The default charset to parse as, if not specified in content-type. Must be
+either `utf-8` or `iso-8859-1`. Defaults to `utf-8`.
+
+##### charsetSentinel
+
+Whether to let the value of the `utf8` parameter take precedence as the charset
+selector. It requires the form to contain a parameter named `utf8` with a value
+of `✓`. Defaults to `false`.
+
+##### interpretNumericEntities
+
+Whether to decode numeric entities such as `☺` when parsing an iso-8859-1
+form. Defaults to `false`.
+
+
+##### depth
+
+The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible.
+
+## Errors
+
+The middlewares provided by this module create errors using the
+[`http-errors` module](https://www.npmjs.com/package/http-errors). The errors
+will typically have a `status`/`statusCode` property that contains the suggested
+HTTP response code, an `expose` property to determine if the `message` property
+should be displayed to the client, a `type` property to determine the type of
+error without matching against the `message`, and a `body` property containing
+the read body, if available.
+
+The following are the common errors created, though any error can come through
+for various reasons.
+
+### content encoding unsupported
+
+This error will occur when the request had a `Content-Encoding` header that
+contained an encoding but the "inflation" option was set to `false`. The
+`status` property is set to `415`, the `type` property is set to
+`'encoding.unsupported'`, and the `charset` property will be set to the
+encoding that is unsupported.
+
+### entity parse failed
+
+This error will occur when the request contained an entity that could not be
+parsed by the middleware. The `status` property is set to `400`, the `type`
+property is set to `'entity.parse.failed'`, and the `body` property is set to
+the entity value that failed parsing.
+
+### entity verify failed
+
+This error will occur when the request contained an entity that could not be
+failed verification by the defined `verify` option. The `status` property is
+set to `403`, the `type` property is set to `'entity.verify.failed'`, and the
+`body` property is set to the entity value that failed verification.
+
+### request aborted
+
+This error will occur when the request is aborted by the client before reading
+the body has finished. The `received` property will be set to the number of
+bytes received before the request was aborted and the `expected` property is
+set to the number of expected bytes. The `status` property is set to `400`
+and `type` property is set to `'request.aborted'`.
+
+### request entity too large
+
+This error will occur when the request body's size is larger than the "limit"
+option. The `limit` property will be set to the byte limit and the `length`
+property will be set to the request body's length. The `status` property is
+set to `413` and the `type` property is set to `'entity.too.large'`.
+
+### request size did not match content length
+
+This error will occur when the request's length did not match the length from
+the `Content-Length` header. This typically occurs when the request is malformed,
+typically when the `Content-Length` header was calculated based on characters
+instead of bytes. The `status` property is set to `400` and the `type` property
+is set to `'request.size.invalid'`.
+
+### stream encoding should not be set
+
+This error will occur when something called the `req.setEncoding` method prior
+to this middleware. This module operates directly on bytes only and you cannot
+call `req.setEncoding` when using this module. The `status` property is set to
+`500` and the `type` property is set to `'stream.encoding.set'`.
+
+### stream is not readable
+
+This error will occur when the request is no longer readable when this middleware
+attempts to read it. This typically means something other than a middleware from
+this module read the request body already and the middleware was also configured to
+read the same request. The `status` property is set to `500` and the `type`
+property is set to `'stream.not.readable'`.
+
+### too many parameters
+
+This error will occur when the content of the request exceeds the configured
+`parameterLimit` for the `urlencoded` parser. The `status` property is set to
+`413` and the `type` property is set to `'parameters.too.many'`.
+
+### unsupported charset "BOGUS"
+
+This error will occur when the request had a charset parameter in the
+`Content-Type` header, but the `iconv-lite` module does not support it OR the
+parser does not support it. The charset is contained in the message as well
+as in the `charset` property. The `status` property is set to `415`, the
+`type` property is set to `'charset.unsupported'`, and the `charset` property
+is set to the charset that is unsupported.
+
+### unsupported content encoding "bogus"
+
+This error will occur when the request had a `Content-Encoding` header that
+contained an unsupported encoding. The encoding is contained in the message
+as well as in the `encoding` property. The `status` property is set to `415`,
+the `type` property is set to `'encoding.unsupported'`, and the `encoding`
+property is set to the encoding that is unsupported.
+
+### The input exceeded the depth
+
+This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown.
+
+## Examples
+
+### Express/Connect top-level generic
+
+This example demonstrates adding a generic JSON and URL-encoded parser as a
+top-level middleware, which will parse the bodies of all incoming requests.
+This is the simplest setup.
+
+```js
+const express = require('express')
+const bodyParser = require('body-parser')
+
+const app = express()
+
+// parse application/x-www-form-urlencoded
+app.use(bodyParser.urlencoded())
+
+// parse application/json
+app.use(bodyParser.json())
+
+app.use(function (req, res) {
+ res.setHeader('Content-Type', 'text/plain')
+ res.write('you posted:\n')
+ res.end(String(JSON.stringify(req.body, null, 2)))
+})
+```
+
+### Express route-specific
+
+This example demonstrates adding body parsers specifically to the routes that
+need them. In general, this is the most recommended way to use body-parser with
+Express.
+
+```js
+const express = require('express')
+const bodyParser = require('body-parser')
+
+const app = express()
+
+// create application/json parser
+const jsonParser = bodyParser.json()
+
+// create application/x-www-form-urlencoded parser
+const urlencodedParser = bodyParser.urlencoded()
+
+// POST /login gets urlencoded bodies
+app.post('/login', urlencodedParser, function (req, res) {
+ if (!req.body || !req.body.username) res.sendStatus(400)
+ res.send('welcome, ' + req.body.username)
+})
+
+// POST /api/users gets JSON bodies
+app.post('/api/users', jsonParser, function (req, res) {
+ if (!req.body) res.sendStatus(400)
+ // create user in req.body
+})
+```
+
+### Change accepted type for parsers
+
+All the parsers accept a `type` option which allows you to change the
+`Content-Type` that the middleware will parse.
+
+```js
+const express = require('express')
+const bodyParser = require('body-parser')
+
+const app = express()
+
+// parse various different custom JSON types as JSON
+app.use(bodyParser.json({ type: 'application/*+json' }))
+
+// parse some custom thing into a Buffer
+app.use(bodyParser.raw({ type: 'application/vnd.custom-type' }))
+
+// parse an HTML body into a string
+app.use(bodyParser.text({ type: 'text/html' }))
+```
+
+## License
+
+[MIT](LICENSE)
+
+[ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/body-parser/ci.yml?branch=master&label=ci
+[ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml
+[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/body-parser?branch=master
+[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master
+[npm-downloads-image]: https://img.shields.io/npm/dm/body-parser
+[npm-url]: https://npmjs.org/package/body-parser
+[npm-version-image]: https://img.shields.io/npm/v/body-parser
+[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/body-parser/badge
+[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/body-parser
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/index.js
new file mode 100644
index 000000000..d722d0b23
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/index.js
@@ -0,0 +1,80 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * @typedef Parsers
+ * @type {function}
+ * @property {function} json
+ * @property {function} raw
+ * @property {function} text
+ * @property {function} urlencoded
+ */
+
+/**
+ * Module exports.
+ * @type {Parsers}
+ */
+
+exports = module.exports = bodyParser
+
+/**
+ * JSON parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'json', {
+ configurable: true,
+ enumerable: true,
+ get: () => require('./lib/types/json')
+})
+
+/**
+ * Raw parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'raw', {
+ configurable: true,
+ enumerable: true,
+ get: () => require('./lib/types/raw')
+})
+
+/**
+ * Text parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'text', {
+ configurable: true,
+ enumerable: true,
+ get: () => require('./lib/types/text')
+})
+
+/**
+ * URL-encoded parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'urlencoded', {
+ configurable: true,
+ enumerable: true,
+ get: () => require('./lib/types/urlencoded')
+})
+
+/**
+ * Create a middleware to parse json and urlencoded bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @deprecated
+ * @public
+ */
+
+function bodyParser () {
+ throw new Error('The bodyParser() generic has been split into individual middleware to use instead.')
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/read.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/read.js
new file mode 100644
index 000000000..b3f2345f2
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/read.js
@@ -0,0 +1,250 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var createError = require('http-errors')
+var getBody = require('raw-body')
+var iconv = require('iconv-lite')
+var onFinished = require('on-finished')
+var zlib = require('node:zlib')
+var hasBody = require('type-is').hasBody
+var { getCharset } = require('./utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = read
+
+/**
+ * Read a request into a buffer and parse.
+ *
+ * @param {object} req
+ * @param {object} res
+ * @param {function} next
+ * @param {function} parse
+ * @param {function} debug
+ * @param {object} options
+ * @private
+ */
+
+function read (req, res, next, parse, debug, options) {
+ if (onFinished.isFinished(req)) {
+ debug('body already parsed')
+ next()
+ return
+ }
+
+ if (!('body' in req)) {
+ req.body = undefined
+ }
+
+ // skip requests without bodies
+ if (!hasBody(req)) {
+ debug('skip empty body')
+ next()
+ return
+ }
+
+ debug('content-type %j', req.headers['content-type'])
+
+ // determine if request should be parsed
+ if (!options.shouldParse(req)) {
+ debug('skip parsing')
+ next()
+ return
+ }
+
+ var encoding = null
+ if (options?.skipCharset !== true) {
+ encoding = getCharset(req) || options.defaultCharset
+
+ // validate charset
+ if (!!options?.isValidCharset && !options.isValidCharset(encoding)) {
+ debug('invalid charset')
+ next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding,
+ type: 'charset.unsupported'
+ }))
+ return
+ }
+ }
+
+ var length
+ var opts = options
+ var stream
+
+ // read options
+ var verify = opts.verify
+
+ try {
+ // get the content stream
+ stream = contentstream(req, debug, opts.inflate)
+ length = stream.length
+ stream.length = undefined
+ } catch (err) {
+ return next(err)
+ }
+
+ // set raw-body options
+ opts.length = length
+ opts.encoding = verify
+ ? null
+ : encoding
+
+ // assert charset is supported
+ if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
+ return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding.toLowerCase(),
+ type: 'charset.unsupported'
+ }))
+ }
+
+ // read body
+ debug('read body')
+ getBody(stream, opts, function (error, body) {
+ if (error) {
+ var _error
+
+ if (error.type === 'encoding.unsupported') {
+ // echo back charset
+ _error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding.toLowerCase(),
+ type: 'charset.unsupported'
+ })
+ } else {
+ // set status code on error
+ _error = createError(400, error)
+ }
+
+ // unpipe from stream and destroy
+ if (stream !== req) {
+ req.unpipe()
+ stream.destroy()
+ }
+
+ // read off entire request
+ dump(req, function onfinished () {
+ next(createError(400, _error))
+ })
+ return
+ }
+
+ // verify
+ if (verify) {
+ try {
+ debug('verify body')
+ verify(req, res, body, encoding)
+ } catch (err) {
+ next(createError(403, err, {
+ body: body,
+ type: err.type || 'entity.verify.failed'
+ }))
+ return
+ }
+ }
+
+ // parse
+ var str = body
+ try {
+ debug('parse body')
+ str = typeof body !== 'string' && encoding !== null
+ ? iconv.decode(body, encoding)
+ : body
+ req.body = parse(str, encoding)
+ } catch (err) {
+ next(createError(400, err, {
+ body: str,
+ type: err.type || 'entity.parse.failed'
+ }))
+ return
+ }
+
+ next()
+ })
+}
+
+/**
+ * Get the content stream of the request.
+ *
+ * @param {object} req
+ * @param {function} debug
+ * @param {boolean} [inflate=true]
+ * @return {object}
+ * @api private
+ */
+
+function contentstream (req, debug, inflate) {
+ var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
+ var length = req.headers['content-length']
+
+ debug('content-encoding "%s"', encoding)
+
+ if (inflate === false && encoding !== 'identity') {
+ throw createError(415, 'content encoding unsupported', {
+ encoding: encoding,
+ type: 'encoding.unsupported'
+ })
+ }
+
+ if (encoding === 'identity') {
+ req.length = length
+ return req
+ }
+
+ var stream = createDecompressionStream(encoding, debug)
+ req.pipe(stream)
+ return stream
+}
+
+/**
+ * Create a decompression stream for the given encoding.
+ * @param {string} encoding
+ * @param {function} debug
+ * @return {object}
+ * @api private
+ */
+function createDecompressionStream (encoding, debug) {
+ switch (encoding) {
+ case 'deflate':
+ debug('inflate body')
+ return zlib.createInflate()
+ case 'gzip':
+ debug('gunzip body')
+ return zlib.createGunzip()
+ case 'br':
+ debug('brotli decompress body')
+ return zlib.createBrotliDecompress()
+ default:
+ throw createError(415, 'unsupported content encoding "' + encoding + '"', {
+ encoding: encoding,
+ type: 'encoding.unsupported'
+ })
+ }
+}
+
+/**
+ * Dump the contents of a request.
+ *
+ * @param {object} req
+ * @param {function} callback
+ * @api private
+ */
+
+function dump (req, callback) {
+ if (onFinished.isFinished(req)) {
+ callback(null)
+ } else {
+ onFinished(req, callback)
+ req.resume()
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/json.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/json.js
new file mode 100644
index 000000000..15c54bb4b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/json.js
@@ -0,0 +1,166 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var debug = require('debug')('body-parser:json')
+var read = require('../read')
+var { normalizeOptions } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = json
+
+/**
+ * RegExp to match the first non-space in a string.
+ *
+ * Allowed whitespace is defined in RFC 7159:
+ *
+ * ws = *(
+ * %x20 / ; Space
+ * %x09 / ; Horizontal tab
+ * %x0A / ; Line feed or New line
+ * %x0D ) ; Carriage return
+ */
+
+var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
+
+var JSON_SYNTAX_CHAR = '#'
+var JSON_SYNTAX_REGEXP = /#+/g
+
+/**
+ * Create a middleware to parse JSON bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @public
+ */
+
+function json (options) {
+ const normalizedOptions = normalizeOptions(options, 'application/json')
+
+ var reviver = options?.reviver
+ var strict = options?.strict !== false
+
+ function parse (body) {
+ if (body.length === 0) {
+ // special-case empty json body, as it's a common client-side mistake
+ // TODO: maybe make this configurable or part of "strict" option
+ return {}
+ }
+
+ if (strict) {
+ var first = firstchar(body)
+
+ if (first !== '{' && first !== '[') {
+ debug('strict violation')
+ throw createStrictSyntaxError(body, first)
+ }
+ }
+
+ try {
+ debug('parse json')
+ return JSON.parse(body, reviver)
+ } catch (e) {
+ throw normalizeJsonSyntaxError(e, {
+ message: e.message,
+ stack: e.stack
+ })
+ }
+ }
+
+ const readOptions = {
+ ...normalizedOptions,
+ // assert charset per RFC 7159 sec 8.1
+ isValidCharset: (charset) => charset.slice(0, 4) === 'utf-'
+ }
+
+ return function jsonParser (req, res, next) {
+ read(req, res, next, parse, debug, readOptions)
+ }
+}
+
+/**
+ * Create strict violation syntax error matching native error.
+ *
+ * @param {string} str
+ * @param {string} char
+ * @return {Error}
+ * @private
+ */
+
+function createStrictSyntaxError (str, char) {
+ var index = str.indexOf(char)
+ var partial = ''
+
+ if (index !== -1) {
+ partial = str.substring(0, index) + JSON_SYNTAX_CHAR
+
+ for (var i = index + 1; i < str.length; i++) {
+ partial += JSON_SYNTAX_CHAR
+ }
+ }
+
+ try {
+ JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
+ } catch (e) {
+ return normalizeJsonSyntaxError(e, {
+ message: e.message.replace(JSON_SYNTAX_REGEXP, function (placeholder) {
+ return str.substring(index, index + placeholder.length)
+ }),
+ stack: e.stack
+ })
+ }
+}
+
+/**
+ * Get the first non-whitespace character in a string.
+ *
+ * @param {string} str
+ * @return {function}
+ * @private
+ */
+
+function firstchar (str) {
+ var match = FIRST_CHAR_REGEXP.exec(str)
+
+ return match
+ ? match[1]
+ : undefined
+}
+
+/**
+ * Normalize a SyntaxError for JSON.parse.
+ *
+ * @param {SyntaxError} error
+ * @param {object} obj
+ * @return {SyntaxError}
+ */
+
+function normalizeJsonSyntaxError (error, obj) {
+ var keys = Object.getOwnPropertyNames(error)
+
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i]
+ if (key !== 'stack' && key !== 'message') {
+ delete error[key]
+ }
+ }
+
+ // replace stack before message for Node.js 0.10 and below
+ error.stack = obj.stack.replace(error.message, obj.message)
+ error.message = obj.message
+
+ return error
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/raw.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/raw.js
new file mode 100644
index 000000000..04b1b88fa
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/raw.js
@@ -0,0 +1,43 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+var debug = require('debug')('body-parser:raw')
+var read = require('../read')
+var { normalizeOptions, passthrough } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = raw
+
+/**
+ * Create a middleware to parse raw bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @api public
+ */
+
+function raw (options) {
+ const normalizedOptions = normalizeOptions(options, 'application/octet-stream')
+
+ const readOptions = {
+ ...normalizedOptions,
+ // Skip charset validation and parse the body as is
+ skipCharset: true
+ }
+
+ return function rawParser (req, res, next) {
+ read(req, res, next, passthrough, debug, readOptions)
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/text.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/text.js
new file mode 100644
index 000000000..d4c7e3b64
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/text.js
@@ -0,0 +1,37 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+var debug = require('debug')('body-parser:text')
+var read = require('../read')
+var { normalizeOptions, passthrough } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = text
+
+/**
+ * Create a middleware to parse text bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @api public
+ */
+
+function text (options) {
+ const normalizedOptions = normalizeOptions(options, 'text/plain')
+
+ return function textParser (req, res, next) {
+ read(req, res, next, passthrough, debug, normalizedOptions)
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/urlencoded.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/urlencoded.js
new file mode 100644
index 000000000..924093736
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/types/urlencoded.js
@@ -0,0 +1,142 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var createError = require('http-errors')
+var debug = require('debug')('body-parser:urlencoded')
+var read = require('../read')
+var qs = require('qs')
+var { normalizeOptions } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = urlencoded
+
+/**
+ * Create a middleware to parse urlencoded bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @public
+ */
+
+function urlencoded (options) {
+ const normalizedOptions = normalizeOptions(options, 'application/x-www-form-urlencoded')
+
+ if (normalizedOptions.defaultCharset !== 'utf-8' && normalizedOptions.defaultCharset !== 'iso-8859-1') {
+ throw new TypeError('option defaultCharset must be either utf-8 or iso-8859-1')
+ }
+
+ // create the appropriate query parser
+ var queryparse = createQueryParser(options)
+
+ function parse (body, encoding) {
+ return body.length
+ ? queryparse(body, encoding)
+ : {}
+ }
+
+ const readOptions = {
+ ...normalizedOptions,
+ // assert charset
+ isValidCharset: (charset) => charset === 'utf-8' || charset === 'iso-8859-1'
+ }
+
+ return function urlencodedParser (req, res, next) {
+ read(req, res, next, parse, debug, readOptions)
+ }
+}
+
+/**
+ * Get the extended query parser.
+ *
+ * @param {object} options
+ */
+
+function createQueryParser (options) {
+ var extended = Boolean(options?.extended)
+ var parameterLimit = options?.parameterLimit !== undefined
+ ? options?.parameterLimit
+ : 1000
+ var charsetSentinel = options?.charsetSentinel
+ var interpretNumericEntities = options?.interpretNumericEntities
+ var depth = extended ? (options?.depth !== undefined ? options?.depth : 32) : 0
+
+ if (isNaN(parameterLimit) || parameterLimit < 1) {
+ throw new TypeError('option parameterLimit must be a positive number')
+ }
+
+ if (isNaN(depth) || depth < 0) {
+ throw new TypeError('option depth must be a zero or a positive number')
+ }
+
+ if (isFinite(parameterLimit)) {
+ parameterLimit = parameterLimit | 0
+ }
+
+ return function queryparse (body, encoding) {
+ var paramCount = parameterCount(body, parameterLimit)
+
+ if (paramCount === undefined) {
+ debug('too many parameters')
+ throw createError(413, 'too many parameters', {
+ type: 'parameters.too.many'
+ })
+ }
+
+ var arrayLimit = extended ? Math.max(100, paramCount) : 0
+
+ debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding')
+ try {
+ return qs.parse(body, {
+ allowPrototypes: true,
+ arrayLimit: arrayLimit,
+ depth: depth,
+ charsetSentinel: charsetSentinel,
+ interpretNumericEntities: interpretNumericEntities,
+ charset: encoding,
+ parameterLimit: parameterLimit,
+ strictDepth: true
+ })
+ } catch (err) {
+ if (err instanceof RangeError) {
+ throw createError(400, 'The input exceeded the depth', {
+ type: 'querystring.parse.rangeError'
+ })
+ } else {
+ throw err
+ }
+ }
+ }
+}
+
+/**
+ * Count the number of parameters, stopping once limit reached
+ *
+ * @param {string} body
+ * @param {number} limit
+ * @return {number|undefined} Returns undefined if limit exceeded
+ * @api private
+ */
+function parameterCount (body, limit) {
+ let count = 0
+ let index = -1
+ do {
+ count++
+ if (count > limit) return undefined // Early exit if limit exceeded
+ index = body.indexOf('&', index + 1)
+ } while (index !== -1)
+ return count
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/utils.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/utils.js
new file mode 100644
index 000000000..5634005f0
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/lib/utils.js
@@ -0,0 +1,96 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+var bytes = require('bytes')
+var contentType = require('content-type')
+var typeis = require('type-is')
+
+/**
+ * Module exports.
+ */
+module.exports = {
+ getCharset,
+ normalizeOptions,
+ passthrough
+}
+
+/**
+ * Get the charset of a request.
+ *
+ * @param {object} req
+ * @api private
+ */
+
+function getCharset (req) {
+ try {
+ return (contentType.parse(req).parameters.charset || '').toLowerCase()
+ } catch {
+ return undefined
+ }
+}
+
+/**
+ * Get the simple type checker.
+ *
+ * @param {string | string[]} type
+ * @return {function}
+ */
+
+function typeChecker (type) {
+ return function checkType (req) {
+ return Boolean(typeis(req, type))
+ }
+}
+
+/**
+ * Normalizes the common options for all parsers.
+ *
+ * @param {object} options options to normalize
+ * @param {string | string[] | function} defaultType default content type(s) or a function to determine it
+ * @returns {object}
+ */
+function normalizeOptions (options, defaultType) {
+ if (!defaultType) {
+ // Parsers must define a default content type
+ throw new TypeError('defaultType must be provided')
+ }
+
+ var inflate = options?.inflate !== false
+ var limit = typeof options?.limit !== 'number'
+ ? bytes.parse(options?.limit || '100kb')
+ : options?.limit
+ var type = options?.type || defaultType
+ var verify = options?.verify || false
+ var defaultCharset = options?.defaultCharset || 'utf-8'
+
+ if (verify !== false && typeof verify !== 'function') {
+ throw new TypeError('option verify must be function')
+ }
+
+ // create the appropriate type checking function
+ var shouldParse = typeof type !== 'function'
+ ? typeChecker(type)
+ : type
+
+ return {
+ inflate,
+ limit,
+ verify,
+ defaultCharset,
+ shouldParse
+ }
+}
+
+/**
+ * Passthrough function that returns input unchanged.
+ * Used by parsers that don't need to transform the data.
+ *
+ * @param {*} value
+ * @return {*}
+ */
+function passthrough (value) {
+ return value
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/package.json
new file mode 100644
index 000000000..596133cd7
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/body-parser/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "body-parser",
+ "description": "Node.js body parsing middleware",
+ "version": "2.2.1",
+ "contributors": [
+ "Douglas Christopher Wilson ",
+ "Jonathan Ong (http://jongleberry.com)"
+ ],
+ "license": "MIT",
+ "repository": "expressjs/body-parser",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ },
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.7.0",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.0",
+ "raw-body": "^3.0.1",
+ "type-is": "^2.0.1"
+ },
+ "devDependencies": {
+ "eslint": "^8.57.1",
+ "eslint-config-standard": "^14.1.1",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-markdown": "^3.0.1",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-promise": "^6.6.0",
+ "eslint-plugin-standard": "^4.1.0",
+ "mocha": "^11.1.0",
+ "nyc": "^17.1.0",
+ "supertest": "^7.0.0"
+ },
+ "files": [
+ "lib/",
+ "LICENSE",
+ "index.js"
+ ],
+ "engines": {
+ "node": ">=18"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "test": "mocha --reporter spec --check-leaks test/",
+ "test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
+ "test-cov": "nyc --reporter=html --reporter=text npm test"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/LICENSE
new file mode 100644
index 000000000..de3226673
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013 Julian Gruber
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/README.md
new file mode 100644
index 000000000..6b4e0e164
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/README.md
@@ -0,0 +1,129 @@
+# brace-expansion
+
+[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
+as known from sh/bash, in JavaScript.
+
+[](http://travis-ci.org/juliangruber/brace-expansion)
+[](https://www.npmjs.org/package/brace-expansion)
+[](https://greenkeeper.io/)
+
+[](https://ci.testling.com/juliangruber/brace-expansion)
+
+## Example
+
+```js
+var expand = require('brace-expansion');
+
+expand('file-{a,b,c}.jpg')
+// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
+
+expand('-v{,,}')
+// => ['-v', '-v', '-v']
+
+expand('file{0..2}.jpg')
+// => ['file0.jpg', 'file1.jpg', 'file2.jpg']
+
+expand('file-{a..c}.jpg')
+// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
+
+expand('file{2..0}.jpg')
+// => ['file2.jpg', 'file1.jpg', 'file0.jpg']
+
+expand('file{0..4..2}.jpg')
+// => ['file0.jpg', 'file2.jpg', 'file4.jpg']
+
+expand('file-{a..e..2}.jpg')
+// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg']
+
+expand('file{00..10..5}.jpg')
+// => ['file00.jpg', 'file05.jpg', 'file10.jpg']
+
+expand('{{A..C},{a..c}}')
+// => ['A', 'B', 'C', 'a', 'b', 'c']
+
+expand('ppp{,config,oe{,conf}}')
+// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']
+```
+
+## API
+
+```js
+var expand = require('brace-expansion');
+```
+
+### var expanded = expand(str)
+
+Return an array of all possible and valid expansions of `str`. If none are
+found, `[str]` is returned.
+
+Valid expansions are:
+
+```js
+/^(.*,)+(.+)?$/
+// {a,b,...}
+```
+
+A comma separated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`.
+
+```js
+/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
+// {x..y[..incr]}
+```
+
+A numeric sequence from `x` to `y` inclusive, with optional increment.
+If `x` or `y` start with a leading `0`, all the numbers will be padded
+to have equal length. Negative numbers and backwards iteration work too.
+
+```js
+/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
+// {x..y[..incr]}
+```
+
+An alphabetic sequence from `x` to `y` inclusive, with optional increment.
+`x` and `y` must be exactly one character, and if given, `incr` must be a
+number.
+
+For compatibility reasons, the string `${` is not eligible for brace expansion.
+
+## Installation
+
+With [npm](https://npmjs.org) do:
+
+```bash
+npm install brace-expansion
+```
+
+## Contributors
+
+- [Julian Gruber](https://github.com/juliangruber)
+- [Isaac Z. Schlueter](https://github.com/isaacs)
+
+## Sponsors
+
+This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)!
+
+Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)!
+
+## License
+
+(MIT)
+
+Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/index.js
new file mode 100644
index 000000000..bd19fe685
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/index.js
@@ -0,0 +1,201 @@
+var concatMap = require('concat-map');
+var balanced = require('balanced-match');
+
+module.exports = expandTop;
+
+var escSlash = '\0SLASH'+Math.random()+'\0';
+var escOpen = '\0OPEN'+Math.random()+'\0';
+var escClose = '\0CLOSE'+Math.random()+'\0';
+var escComma = '\0COMMA'+Math.random()+'\0';
+var escPeriod = '\0PERIOD'+Math.random()+'\0';
+
+function numeric(str) {
+ return parseInt(str, 10) == str
+ ? parseInt(str, 10)
+ : str.charCodeAt(0);
+}
+
+function escapeBraces(str) {
+ return str.split('\\\\').join(escSlash)
+ .split('\\{').join(escOpen)
+ .split('\\}').join(escClose)
+ .split('\\,').join(escComma)
+ .split('\\.').join(escPeriod);
+}
+
+function unescapeBraces(str) {
+ return str.split(escSlash).join('\\')
+ .split(escOpen).join('{')
+ .split(escClose).join('}')
+ .split(escComma).join(',')
+ .split(escPeriod).join('.');
+}
+
+
+// Basically just str.split(","), but handling cases
+// where we have nested braced sections, which should be
+// treated as individual members, like {a,{b,c},d}
+function parseCommaParts(str) {
+ if (!str)
+ return [''];
+
+ var parts = [];
+ var m = balanced('{', '}', str);
+
+ if (!m)
+ return str.split(',');
+
+ var pre = m.pre;
+ var body = m.body;
+ var post = m.post;
+ var p = pre.split(',');
+
+ p[p.length-1] += '{' + body + '}';
+ var postParts = parseCommaParts(post);
+ if (post.length) {
+ p[p.length-1] += postParts.shift();
+ p.push.apply(p, postParts);
+ }
+
+ parts.push.apply(parts, p);
+
+ return parts;
+}
+
+function expandTop(str) {
+ if (!str)
+ return [];
+
+ // I don't know why Bash 4.3 does this, but it does.
+ // Anything starting with {} will have the first two bytes preserved
+ // but *only* at the top level, so {},a}b will not expand to anything,
+ // but a{},b}c will be expanded to [a}c,abc].
+ // One could argue that this is a bug in Bash, but since the goal of
+ // this module is to match Bash's rules, we escape a leading {}
+ if (str.substr(0, 2) === '{}') {
+ str = '\\{\\}' + str.substr(2);
+ }
+
+ return expand(escapeBraces(str), true).map(unescapeBraces);
+}
+
+function identity(e) {
+ return e;
+}
+
+function embrace(str) {
+ return '{' + str + '}';
+}
+function isPadded(el) {
+ return /^-?0\d/.test(el);
+}
+
+function lte(i, y) {
+ return i <= y;
+}
+function gte(i, y) {
+ return i >= y;
+}
+
+function expand(str, isTop) {
+ var expansions = [];
+
+ var m = balanced('{', '}', str);
+ if (!m || /\$$/.test(m.pre)) return [str];
+
+ var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
+ var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
+ var isSequence = isNumericSequence || isAlphaSequence;
+ var isOptions = m.body.indexOf(',') >= 0;
+ if (!isSequence && !isOptions) {
+ // {a},b}
+ if (m.post.match(/,(?!,).*\}/)) {
+ str = m.pre + '{' + m.body + escClose + m.post;
+ return expand(str);
+ }
+ return [str];
+ }
+
+ var n;
+ if (isSequence) {
+ n = m.body.split(/\.\./);
+ } else {
+ n = parseCommaParts(m.body);
+ if (n.length === 1) {
+ // x{{a,b}}y ==> x{a}y x{b}y
+ n = expand(n[0], false).map(embrace);
+ if (n.length === 1) {
+ var post = m.post.length
+ ? expand(m.post, false)
+ : [''];
+ return post.map(function(p) {
+ return m.pre + n[0] + p;
+ });
+ }
+ }
+ }
+
+ // at this point, n is the parts, and we know it's not a comma set
+ // with a single entry.
+
+ // no need to expand pre, since it is guaranteed to be free of brace-sets
+ var pre = m.pre;
+ var post = m.post.length
+ ? expand(m.post, false)
+ : [''];
+
+ var N;
+
+ if (isSequence) {
+ var x = numeric(n[0]);
+ var y = numeric(n[1]);
+ var width = Math.max(n[0].length, n[1].length)
+ var incr = n.length == 3
+ ? Math.abs(numeric(n[2]))
+ : 1;
+ var test = lte;
+ var reverse = y < x;
+ if (reverse) {
+ incr *= -1;
+ test = gte;
+ }
+ var pad = n.some(isPadded);
+
+ N = [];
+
+ for (var i = x; test(i, y); i += incr) {
+ var c;
+ if (isAlphaSequence) {
+ c = String.fromCharCode(i);
+ if (c === '\\')
+ c = '';
+ } else {
+ c = String(i);
+ if (pad) {
+ var need = width - c.length;
+ if (need > 0) {
+ var z = new Array(need + 1).join('0');
+ if (i < 0)
+ c = '-' + z + c.slice(1);
+ else
+ c = z + c;
+ }
+ }
+ }
+ N.push(c);
+ }
+ } else {
+ N = concatMap(n, function(el) { return expand(el, false) });
+ }
+
+ for (var j = 0; j < N.length; j++) {
+ for (var k = 0; k < post.length; k++) {
+ var expansion = pre + N[j] + post[k];
+ if (!isTop || isSequence || expansion)
+ expansions.push(expansion);
+ }
+ }
+
+ return expansions;
+}
+
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/package.json
new file mode 100644
index 000000000..344788817
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/brace-expansion/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "brace-expansion",
+ "description": "Brace expansion as known from sh/bash",
+ "version": "1.1.12",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/juliangruber/brace-expansion.git"
+ },
+ "homepage": "https://github.com/juliangruber/brace-expansion",
+ "main": "index.js",
+ "scripts": {
+ "test": "tape test/*.js",
+ "gentest": "bash test/generate.sh",
+ "bench": "matcha test/perf/bench.js"
+ },
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ },
+ "devDependencies": {
+ "matcha": "^0.7.0",
+ "tape": "^4.6.0"
+ },
+ "keywords": [],
+ "author": {
+ "name": "Julian Gruber",
+ "email": "mail@juliangruber.com",
+ "url": "http://juliangruber.com"
+ },
+ "license": "MIT",
+ "testling": {
+ "files": "test/*.js",
+ "browsers": [
+ "ie/8..latest",
+ "firefox/20..latest",
+ "firefox/nightly",
+ "chrome/25..latest",
+ "chrome/canary",
+ "opera/12..latest",
+ "opera/next",
+ "safari/5.1..latest",
+ "ipad/6.0..latest",
+ "iphone/6.0..latest",
+ "android-browser/4.2..latest"
+ ]
+ },
+ "publishConfig": {
+ "tag": "1.x"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/AUTHORS.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/AUTHORS.md
new file mode 100644
index 000000000..22eb17129
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/AUTHORS.md
@@ -0,0 +1,70 @@
+# Authors
+
+#### Ordered by first contribution.
+
+- Romain Beauxis (toots@rastageeks.org)
+- Tobias Koppers (tobias.koppers@googlemail.com)
+- Janus (ysangkok@gmail.com)
+- Rainer Dreyer (rdrey1@gmail.com)
+- Tõnis Tiigi (tonistiigi@gmail.com)
+- James Halliday (mail@substack.net)
+- Michael Williamson (mike@zwobble.org)
+- elliottcable (github@elliottcable.name)
+- rafael (rvalle@livelens.net)
+- Andrew Kelley (superjoe30@gmail.com)
+- Andreas Madsen (amwebdk@gmail.com)
+- Mike Brevoort (mike.brevoort@pearson.com)
+- Brian White (mscdex@mscdex.net)
+- Feross Aboukhadijeh (feross@feross.org)
+- Ruben Verborgh (ruben@verborgh.org)
+- eliang (eliang.cs@gmail.com)
+- Jesse Tane (jesse.tane@gmail.com)
+- Alfonso Boza (alfonso@cloud.com)
+- Mathias Buus (mathiasbuus@gmail.com)
+- Devon Govett (devongovett@gmail.com)
+- Daniel Cousens (github@dcousens.com)
+- Joseph Dykstra (josephdykstra@gmail.com)
+- Parsha Pourkhomami (parshap+git@gmail.com)
+- Damjan Košir (damjan.kosir@gmail.com)
+- daverayment (dave.rayment@gmail.com)
+- kawanet (u-suke@kawa.net)
+- Linus Unnebäck (linus@folkdatorn.se)
+- Nolan Lawson (nolan.lawson@gmail.com)
+- Calvin Metcalf (calvin.metcalf@gmail.com)
+- Koki Takahashi (hakatasiloving@gmail.com)
+- Guy Bedford (guybedford@gmail.com)
+- Jan Schär (jscissr@gmail.com)
+- RaulTsc (tomescu.raul@gmail.com)
+- Matthieu Monsch (monsch@alum.mit.edu)
+- Dan Ehrenberg (littledan@chromium.org)
+- Kirill Fomichev (fanatid@ya.ru)
+- Yusuke Kawasaki (u-suke@kawa.net)
+- DC (dcposch@dcpos.ch)
+- John-David Dalton (john.david.dalton@gmail.com)
+- adventure-yunfei (adventure030@gmail.com)
+- Emil Bay (github@tixz.dk)
+- Sam Sudar (sudar.sam@gmail.com)
+- Volker Mische (volker.mische@gmail.com)
+- David Walton (support@geekstocks.com)
+- Сковорода Никита Андреевич (chalkerx@gmail.com)
+- greenkeeper[bot] (greenkeeper[bot]@users.noreply.github.com)
+- ukstv (sergey.ukustov@machinomy.com)
+- Renée Kooi (renee@kooi.me)
+- ranbochen (ranbochen@qq.com)
+- Vladimir Borovik (bobahbdb@gmail.com)
+- greenkeeper[bot] (23040076+greenkeeper[bot]@users.noreply.github.com)
+- kumavis (aaron@kumavis.me)
+- Sergey Ukustov (sergey.ukustov@machinomy.com)
+- Fei Liu (liu.feiwood@gmail.com)
+- Blaine Bublitz (blaine.bublitz@gmail.com)
+- clement (clement@seald.io)
+- Koushik Dutta (koushd@gmail.com)
+- Jordan Harband (ljharb@gmail.com)
+- Niklas Mischkulnig (mischnic@users.noreply.github.com)
+- Nikolai Vavilov (vvnicholas@gmail.com)
+- Fedor Nezhivoi (gyzerok@users.noreply.github.com)
+- Peter Newman (peternewman@users.noreply.github.com)
+- mathmakgakpak (44949126+mathmakgakpak@users.noreply.github.com)
+- jkkang (jkkang@smartauth.kr)
+
+#### Generated by bin/update-authors.sh.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/LICENSE
new file mode 100644
index 000000000..d6bf75dcf
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Feross Aboukhadijeh, and other contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/README.md
new file mode 100644
index 000000000..9a23d7cfa
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/README.md
@@ -0,0 +1,410 @@
+# buffer [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url]
+
+[travis-image]: https://img.shields.io/travis/feross/buffer/master.svg
+[travis-url]: https://travis-ci.org/feross/buffer
+[npm-image]: https://img.shields.io/npm/v/buffer.svg
+[npm-url]: https://npmjs.org/package/buffer
+[downloads-image]: https://img.shields.io/npm/dm/buffer.svg
+[downloads-url]: https://npmjs.org/package/buffer
+[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
+[standard-url]: https://standardjs.com
+
+#### The buffer module from [node.js](https://nodejs.org/), for the browser.
+
+[![saucelabs][saucelabs-image]][saucelabs-url]
+
+[saucelabs-image]: https://saucelabs.com/browser-matrix/buffer.svg
+[saucelabs-url]: https://saucelabs.com/u/buffer
+
+With [browserify](http://browserify.org), simply `require('buffer')` or use the `Buffer` global and you will get this module.
+
+The goal is to provide an API that is 100% identical to
+[node's Buffer API](https://nodejs.org/api/buffer.html). Read the
+[official docs](https://nodejs.org/api/buffer.html) for the full list of properties,
+instance methods, and class methods that are supported.
+
+## features
+
+- Manipulate binary data like a boss, in all browsers!
+- Super fast. Backed by Typed Arrays (`Uint8Array`/`ArrayBuffer`, not `Object`)
+- Extremely small bundle size (**6.75KB minified + gzipped**, 51.9KB with comments)
+- Excellent browser support (Chrome, Firefox, Edge, Safari 9+, IE 11, iOS 9+, Android, etc.)
+- Preserves Node API exactly, with one minor difference (see below)
+- Square-bracket `buf[4]` notation works!
+- Does not modify any browser prototypes or put anything on `window`
+- Comprehensive test suite (including all buffer tests from node.js core)
+
+## install
+
+To use this module directly (without browserify), install it:
+
+```bash
+npm install buffer
+```
+
+This module was previously called **native-buffer-browserify**, but please use **buffer**
+from now on.
+
+If you do not use a bundler, you can use the [standalone script](https://bundle.run/buffer).
+
+## usage
+
+The module's API is identical to node's `Buffer` API. Read the
+[official docs](https://nodejs.org/api/buffer.html) for the full list of properties,
+instance methods, and class methods that are supported.
+
+As mentioned above, `require('buffer')` or use the `Buffer` global with
+[browserify](http://browserify.org) and this module will automatically be included
+in your bundle. Almost any npm module will work in the browser, even if it assumes that
+the node `Buffer` API will be available.
+
+To depend on this module explicitly (without browserify), require it like this:
+
+```js
+var Buffer = require('buffer/').Buffer // note: the trailing slash is important!
+```
+
+To require this module explicitly, use `require('buffer/')` which tells the node.js module
+lookup algorithm (also used by browserify) to use the **npm module** named `buffer`
+instead of the **node.js core** module named `buffer`!
+
+
+## how does it work?
+
+The Buffer constructor returns instances of `Uint8Array` that have their prototype
+changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of `Uint8Array`,
+so the returned instances will have all the node `Buffer` methods and the
+`Uint8Array` methods. Square bracket notation works as expected -- it returns a
+single octet.
+
+The `Uint8Array` prototype remains unmodified.
+
+
+## tracking the latest node api
+
+This module tracks the Buffer API in the latest (unstable) version of node.js. The Buffer
+API is considered **stable** in the
+[node stability index](https://nodejs.org/docs/latest/api/documentation.html#documentation_stability_index),
+so it is unlikely that there will ever be breaking changes.
+Nonetheless, when/if the Buffer API changes in node, this module's API will change
+accordingly.
+
+## related packages
+
+- [`buffer-reverse`](https://www.npmjs.com/package/buffer-reverse) - Reverse a buffer
+- [`buffer-xor`](https://www.npmjs.com/package/buffer-xor) - Bitwise xor a buffer
+- [`is-buffer`](https://www.npmjs.com/package/is-buffer) - Determine if an object is a Buffer without including the whole `Buffer` package
+
+## conversion packages
+
+### convert typed array to buffer
+
+Use [`typedarray-to-buffer`](https://www.npmjs.com/package/typedarray-to-buffer) to convert any kind of typed array to a `Buffer`. Does not perform a copy, so it's super fast.
+
+### convert buffer to typed array
+
+`Buffer` is a subclass of `Uint8Array` (which is a typed array). So there is no need to explicitly convert to typed array. Just use the buffer as a `Uint8Array`.
+
+### convert blob to buffer
+
+Use [`blob-to-buffer`](https://www.npmjs.com/package/blob-to-buffer) to convert a `Blob` to a `Buffer`.
+
+### convert buffer to blob
+
+To convert a `Buffer` to a `Blob`, use the `Blob` constructor:
+
+```js
+var blob = new Blob([ buffer ])
+```
+
+Optionally, specify a mimetype:
+
+```js
+var blob = new Blob([ buffer ], { type: 'text/html' })
+```
+
+### convert arraybuffer to buffer
+
+To convert an `ArrayBuffer` to a `Buffer`, use the `Buffer.from` function. Does not perform a copy, so it's super fast.
+
+```js
+var buffer = Buffer.from(arrayBuffer)
+```
+
+### convert buffer to arraybuffer
+
+To convert a `Buffer` to an `ArrayBuffer`, use the `.buffer` property (which is present on all `Uint8Array` objects):
+
+```js
+var arrayBuffer = buffer.buffer.slice(
+ buffer.byteOffset, buffer.byteOffset + buffer.byteLength
+)
+```
+
+Alternatively, use the [`to-arraybuffer`](https://www.npmjs.com/package/to-arraybuffer) module.
+
+## performance
+
+See perf tests in `/perf`.
+
+`BrowserBuffer` is the browser `buffer` module (this repo). `Uint8Array` is included as a
+sanity check (since `BrowserBuffer` uses `Uint8Array` under the hood, `Uint8Array` will
+always be at least a bit faster). Finally, `NodeBuffer` is the node.js buffer module,
+which is included to compare against.
+
+NOTE: Performance has improved since these benchmarks were taken. PR welcome to update the README.
+
+### Chrome 38
+
+| Method | Operations | Accuracy | Sampled | Fastest |
+|:-------|:-----------|:---------|:--------|:-------:|
+| BrowserBuffer#bracket-notation | 11,457,464 ops/sec | ±0.86% | 66 | ✓ |
+| Uint8Array#bracket-notation | 10,824,332 ops/sec | ±0.74% | 65 | |
+| | | | |
+| BrowserBuffer#concat | 450,532 ops/sec | ±0.76% | 68 | |
+| Uint8Array#concat | 1,368,911 ops/sec | ±1.50% | 62 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16000) | 903,001 ops/sec | ±0.96% | 67 | |
+| Uint8Array#copy(16000) | 1,422,441 ops/sec | ±1.04% | 66 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16) | 11,431,358 ops/sec | ±0.46% | 69 | |
+| Uint8Array#copy(16) | 13,944,163 ops/sec | ±1.12% | 68 | ✓ |
+| | | | |
+| BrowserBuffer#new(16000) | 106,329 ops/sec | ±6.70% | 44 | |
+| Uint8Array#new(16000) | 131,001 ops/sec | ±2.85% | 31 | ✓ |
+| | | | |
+| BrowserBuffer#new(16) | 1,554,491 ops/sec | ±1.60% | 65 | |
+| Uint8Array#new(16) | 6,623,930 ops/sec | ±1.66% | 65 | ✓ |
+| | | | |
+| BrowserBuffer#readDoubleBE | 112,830 ops/sec | ±0.51% | 69 | ✓ |
+| DataView#getFloat64 | 93,500 ops/sec | ±0.57% | 68 | |
+| | | | |
+| BrowserBuffer#readFloatBE | 146,678 ops/sec | ±0.95% | 68 | ✓ |
+| DataView#getFloat32 | 99,311 ops/sec | ±0.41% | 67 | |
+| | | | |
+| BrowserBuffer#readUInt32LE | 843,214 ops/sec | ±0.70% | 69 | ✓ |
+| DataView#getUint32 | 103,024 ops/sec | ±0.64% | 67 | |
+| | | | |
+| BrowserBuffer#slice | 1,013,941 ops/sec | ±0.75% | 67 | |
+| Uint8Array#subarray | 1,903,928 ops/sec | ±0.53% | 67 | ✓ |
+| | | | |
+| BrowserBuffer#writeFloatBE | 61,387 ops/sec | ±0.90% | 67 | |
+| DataView#setFloat32 | 141,249 ops/sec | ±0.40% | 66 | ✓ |
+
+
+### Firefox 33
+
+| Method | Operations | Accuracy | Sampled | Fastest |
+|:-------|:-----------|:---------|:--------|:-------:|
+| BrowserBuffer#bracket-notation | 20,800,421 ops/sec | ±1.84% | 60 | |
+| Uint8Array#bracket-notation | 20,826,235 ops/sec | ±2.02% | 61 | ✓ |
+| | | | |
+| BrowserBuffer#concat | 153,076 ops/sec | ±2.32% | 61 | |
+| Uint8Array#concat | 1,255,674 ops/sec | ±8.65% | 52 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16000) | 1,105,312 ops/sec | ±1.16% | 63 | |
+| Uint8Array#copy(16000) | 1,615,911 ops/sec | ±0.55% | 66 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16) | 16,357,599 ops/sec | ±0.73% | 68 | |
+| Uint8Array#copy(16) | 31,436,281 ops/sec | ±1.05% | 68 | ✓ |
+| | | | |
+| BrowserBuffer#new(16000) | 52,995 ops/sec | ±6.01% | 35 | |
+| Uint8Array#new(16000) | 87,686 ops/sec | ±5.68% | 45 | ✓ |
+| | | | |
+| BrowserBuffer#new(16) | 252,031 ops/sec | ±1.61% | 66 | |
+| Uint8Array#new(16) | 8,477,026 ops/sec | ±0.49% | 68 | ✓ |
+| | | | |
+| BrowserBuffer#readDoubleBE | 99,871 ops/sec | ±0.41% | 69 | |
+| DataView#getFloat64 | 285,663 ops/sec | ±0.70% | 68 | ✓ |
+| | | | |
+| BrowserBuffer#readFloatBE | 115,540 ops/sec | ±0.42% | 69 | |
+| DataView#getFloat32 | 288,722 ops/sec | ±0.82% | 68 | ✓ |
+| | | | |
+| BrowserBuffer#readUInt32LE | 633,926 ops/sec | ±1.08% | 67 | ✓ |
+| DataView#getUint32 | 294,808 ops/sec | ±0.79% | 64 | |
+| | | | |
+| BrowserBuffer#slice | 349,425 ops/sec | ±0.46% | 69 | |
+| Uint8Array#subarray | 5,965,819 ops/sec | ±0.60% | 65 | ✓ |
+| | | | |
+| BrowserBuffer#writeFloatBE | 59,980 ops/sec | ±0.41% | 67 | |
+| DataView#setFloat32 | 317,634 ops/sec | ±0.63% | 68 | ✓ |
+
+### Safari 8
+
+| Method | Operations | Accuracy | Sampled | Fastest |
+|:-------|:-----------|:---------|:--------|:-------:|
+| BrowserBuffer#bracket-notation | 10,279,729 ops/sec | ±2.25% | 56 | ✓ |
+| Uint8Array#bracket-notation | 10,030,767 ops/sec | ±2.23% | 59 | |
+| | | | |
+| BrowserBuffer#concat | 144,138 ops/sec | ±1.38% | 65 | |
+| Uint8Array#concat | 4,950,764 ops/sec | ±1.70% | 63 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16000) | 1,058,548 ops/sec | ±1.51% | 64 | |
+| Uint8Array#copy(16000) | 1,409,666 ops/sec | ±1.17% | 65 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16) | 6,282,529 ops/sec | ±1.88% | 58 | |
+| Uint8Array#copy(16) | 11,907,128 ops/sec | ±2.87% | 58 | ✓ |
+| | | | |
+| BrowserBuffer#new(16000) | 101,663 ops/sec | ±3.89% | 57 | |
+| Uint8Array#new(16000) | 22,050,818 ops/sec | ±6.51% | 46 | ✓ |
+| | | | |
+| BrowserBuffer#new(16) | 176,072 ops/sec | ±2.13% | 64 | |
+| Uint8Array#new(16) | 24,385,731 ops/sec | ±5.01% | 51 | ✓ |
+| | | | |
+| BrowserBuffer#readDoubleBE | 41,341 ops/sec | ±1.06% | 67 | |
+| DataView#getFloat64 | 322,280 ops/sec | ±0.84% | 68 | ✓ |
+| | | | |
+| BrowserBuffer#readFloatBE | 46,141 ops/sec | ±1.06% | 65 | |
+| DataView#getFloat32 | 337,025 ops/sec | ±0.43% | 69 | ✓ |
+| | | | |
+| BrowserBuffer#readUInt32LE | 151,551 ops/sec | ±1.02% | 66 | |
+| DataView#getUint32 | 308,278 ops/sec | ±0.94% | 67 | ✓ |
+| | | | |
+| BrowserBuffer#slice | 197,365 ops/sec | ±0.95% | 66 | |
+| Uint8Array#subarray | 9,558,024 ops/sec | ±3.08% | 58 | ✓ |
+| | | | |
+| BrowserBuffer#writeFloatBE | 17,518 ops/sec | ±1.03% | 63 | |
+| DataView#setFloat32 | 319,751 ops/sec | ±0.48% | 68 | ✓ |
+
+
+### Node 0.11.14
+
+| Method | Operations | Accuracy | Sampled | Fastest |
+|:-------|:-----------|:---------|:--------|:-------:|
+| BrowserBuffer#bracket-notation | 10,489,828 ops/sec | ±3.25% | 90 | |
+| Uint8Array#bracket-notation | 10,534,884 ops/sec | ±0.81% | 92 | ✓ |
+| NodeBuffer#bracket-notation | 10,389,910 ops/sec | ±0.97% | 87 | |
+| | | | |
+| BrowserBuffer#concat | 487,830 ops/sec | ±2.58% | 88 | |
+| Uint8Array#concat | 1,814,327 ops/sec | ±1.28% | 88 | ✓ |
+| NodeBuffer#concat | 1,636,523 ops/sec | ±1.88% | 73 | |
+| | | | |
+| BrowserBuffer#copy(16000) | 1,073,665 ops/sec | ±0.77% | 90 | |
+| Uint8Array#copy(16000) | 1,348,517 ops/sec | ±0.84% | 89 | ✓ |
+| NodeBuffer#copy(16000) | 1,289,533 ops/sec | ±0.82% | 93 | |
+| | | | |
+| BrowserBuffer#copy(16) | 12,782,706 ops/sec | ±0.74% | 85 | |
+| Uint8Array#copy(16) | 14,180,427 ops/sec | ±0.93% | 92 | ✓ |
+| NodeBuffer#copy(16) | 11,083,134 ops/sec | ±1.06% | 89 | |
+| | | | |
+| BrowserBuffer#new(16000) | 141,678 ops/sec | ±3.30% | 67 | |
+| Uint8Array#new(16000) | 161,491 ops/sec | ±2.96% | 60 | |
+| NodeBuffer#new(16000) | 292,699 ops/sec | ±3.20% | 55 | ✓ |
+| | | | |
+| BrowserBuffer#new(16) | 1,655,466 ops/sec | ±2.41% | 82 | |
+| Uint8Array#new(16) | 14,399,926 ops/sec | ±0.91% | 94 | ✓ |
+| NodeBuffer#new(16) | 3,894,696 ops/sec | ±0.88% | 92 | |
+| | | | |
+| BrowserBuffer#readDoubleBE | 109,582 ops/sec | ±0.75% | 93 | ✓ |
+| DataView#getFloat64 | 91,235 ops/sec | ±0.81% | 90 | |
+| NodeBuffer#readDoubleBE | 88,593 ops/sec | ±0.96% | 81 | |
+| | | | |
+| BrowserBuffer#readFloatBE | 139,854 ops/sec | ±1.03% | 85 | ✓ |
+| DataView#getFloat32 | 98,744 ops/sec | ±0.80% | 89 | |
+| NodeBuffer#readFloatBE | 92,769 ops/sec | ±0.94% | 93 | |
+| | | | |
+| BrowserBuffer#readUInt32LE | 710,861 ops/sec | ±0.82% | 92 | |
+| DataView#getUint32 | 117,893 ops/sec | ±0.84% | 91 | |
+| NodeBuffer#readUInt32LE | 851,412 ops/sec | ±0.72% | 93 | ✓ |
+| | | | |
+| BrowserBuffer#slice | 1,673,877 ops/sec | ±0.73% | 94 | |
+| Uint8Array#subarray | 6,919,243 ops/sec | ±0.67% | 90 | ✓ |
+| NodeBuffer#slice | 4,617,604 ops/sec | ±0.79% | 93 | |
+| | | | |
+| BrowserBuffer#writeFloatBE | 66,011 ops/sec | ±0.75% | 93 | |
+| DataView#setFloat32 | 127,760 ops/sec | ±0.72% | 93 | ✓ |
+| NodeBuffer#writeFloatBE | 103,352 ops/sec | ±0.83% | 93 | |
+
+### iojs 1.8.1
+
+| Method | Operations | Accuracy | Sampled | Fastest |
+|:-------|:-----------|:---------|:--------|:-------:|
+| BrowserBuffer#bracket-notation | 10,990,488 ops/sec | ±1.11% | 91 | |
+| Uint8Array#bracket-notation | 11,268,757 ops/sec | ±0.65% | 97 | |
+| NodeBuffer#bracket-notation | 11,353,260 ops/sec | ±0.83% | 94 | ✓ |
+| | | | |
+| BrowserBuffer#concat | 378,954 ops/sec | ±0.74% | 94 | |
+| Uint8Array#concat | 1,358,288 ops/sec | ±0.97% | 87 | |
+| NodeBuffer#concat | 1,934,050 ops/sec | ±1.11% | 78 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16000) | 894,538 ops/sec | ±0.56% | 84 | |
+| Uint8Array#copy(16000) | 1,442,656 ops/sec | ±0.71% | 96 | |
+| NodeBuffer#copy(16000) | 1,457,898 ops/sec | ±0.53% | 92 | ✓ |
+| | | | |
+| BrowserBuffer#copy(16) | 12,870,457 ops/sec | ±0.67% | 95 | |
+| Uint8Array#copy(16) | 16,643,989 ops/sec | ±0.61% | 93 | ✓ |
+| NodeBuffer#copy(16) | 14,885,848 ops/sec | ±0.74% | 94 | |
+| | | | |
+| BrowserBuffer#new(16000) | 109,264 ops/sec | ±4.21% | 63 | |
+| Uint8Array#new(16000) | 138,916 ops/sec | ±1.87% | 61 | |
+| NodeBuffer#new(16000) | 281,449 ops/sec | ±3.58% | 51 | ✓ |
+| | | | |
+| BrowserBuffer#new(16) | 1,362,935 ops/sec | ±0.56% | 99 | |
+| Uint8Array#new(16) | 6,193,090 ops/sec | ±0.64% | 95 | ✓ |
+| NodeBuffer#new(16) | 4,745,425 ops/sec | ±1.56% | 90 | |
+| | | | |
+| BrowserBuffer#readDoubleBE | 118,127 ops/sec | ±0.59% | 93 | ✓ |
+| DataView#getFloat64 | 107,332 ops/sec | ±0.65% | 91 | |
+| NodeBuffer#readDoubleBE | 116,274 ops/sec | ±0.94% | 95 | |
+| | | | |
+| BrowserBuffer#readFloatBE | 150,326 ops/sec | ±0.58% | 95 | ✓ |
+| DataView#getFloat32 | 110,541 ops/sec | ±0.57% | 98 | |
+| NodeBuffer#readFloatBE | 121,599 ops/sec | ±0.60% | 87 | |
+| | | | |
+| BrowserBuffer#readUInt32LE | 814,147 ops/sec | ±0.62% | 93 | |
+| DataView#getUint32 | 137,592 ops/sec | ±0.64% | 90 | |
+| NodeBuffer#readUInt32LE | 931,650 ops/sec | ±0.71% | 96 | ✓ |
+| | | | |
+| BrowserBuffer#slice | 878,590 ops/sec | ±0.68% | 93 | |
+| Uint8Array#subarray | 2,843,308 ops/sec | ±1.02% | 90 | |
+| NodeBuffer#slice | 4,998,316 ops/sec | ±0.68% | 90 | ✓ |
+| | | | |
+| BrowserBuffer#writeFloatBE | 65,927 ops/sec | ±0.74% | 93 | |
+| DataView#setFloat32 | 139,823 ops/sec | ±0.97% | 89 | ✓ |
+| NodeBuffer#writeFloatBE | 135,763 ops/sec | ±0.65% | 96 | |
+| | | | |
+
+## Testing the project
+
+First, install the project:
+
+ npm install
+
+Then, to run tests in Node.js, run:
+
+ npm run test-node
+
+To test locally in a browser, you can run:
+
+ npm run test-browser-es5-local # For ES5 browsers that don't support ES6
+ npm run test-browser-es6-local # For ES6 compliant browsers
+
+This will print out a URL that you can then open in a browser to run the tests, using [airtap](https://www.npmjs.com/package/airtap).
+
+To run automated browser tests using Saucelabs, ensure that your `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY` environment variables are set, then run:
+
+ npm test
+
+This is what's run in Travis, to check against various browsers. The list of browsers is kept in the `bin/airtap-es5.yml` and `bin/airtap-es6.yml` files.
+
+## JavaScript Standard Style
+
+This module uses [JavaScript Standard Style](https://github.com/feross/standard).
+
+[](https://github.com/feross/standard)
+
+To test that the code conforms to the style, `npm install` and run:
+
+ ./node_modules/.bin/standard
+
+## credit
+
+This was originally forked from [buffer-browserify](https://github.com/toots/buffer-browserify).
+
+## Security Policies and Procedures
+
+The `buffer` team and community take all security bugs in `buffer` seriously. Please see our [security policies and procedures](https://github.com/feross/security) document to learn how to report issues.
+
+## license
+
+MIT. Copyright (C) [Feross Aboukhadijeh](http://feross.org), and other contributors. Originally forked from an MIT-licensed module by Romain Beauxis.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/index.d.ts
new file mode 100644
index 000000000..5d1a804e5
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/index.d.ts
@@ -0,0 +1,186 @@
+export class Buffer extends Uint8Array {
+ length: number
+ write(string: string, offset?: number, length?: number, encoding?: string): number;
+ toString(encoding?: string, start?: number, end?: number): string;
+ toJSON(): { type: 'Buffer', data: any[] };
+ equals(otherBuffer: Buffer): boolean;
+ compare(otherBuffer: Buffer, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number;
+ copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;
+ slice(start?: number, end?: number): Buffer;
+ writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;
+ writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;
+ writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;
+ writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;
+ readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number;
+ readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number;
+ readIntLE(offset: number, byteLength: number, noAssert?: boolean): number;
+ readIntBE(offset: number, byteLength: number, noAssert?: boolean): number;
+ readUInt8(offset: number, noAssert?: boolean): number;
+ readUInt16LE(offset: number, noAssert?: boolean): number;
+ readUInt16BE(offset: number, noAssert?: boolean): number;
+ readUInt32LE(offset: number, noAssert?: boolean): number;
+ readUInt32BE(offset: number, noAssert?: boolean): number;
+ readInt8(offset: number, noAssert?: boolean): number;
+ readInt16LE(offset: number, noAssert?: boolean): number;
+ readInt16BE(offset: number, noAssert?: boolean): number;
+ readInt32LE(offset: number, noAssert?: boolean): number;
+ readInt32BE(offset: number, noAssert?: boolean): number;
+ readFloatLE(offset: number, noAssert?: boolean): number;
+ readFloatBE(offset: number, noAssert?: boolean): number;
+ readDoubleLE(offset: number, noAssert?: boolean): number;
+ readDoubleBE(offset: number, noAssert?: boolean): number;
+ reverse(): this;
+ swap16(): Buffer;
+ swap32(): Buffer;
+ swap64(): Buffer;
+ writeUInt8(value: number, offset: number, noAssert?: boolean): number;
+ writeUInt16LE(value: number, offset: number, noAssert?: boolean): number;
+ writeUInt16BE(value: number, offset: number, noAssert?: boolean): number;
+ writeUInt32LE(value: number, offset: number, noAssert?: boolean): number;
+ writeUInt32BE(value: number, offset: number, noAssert?: boolean): number;
+ writeInt8(value: number, offset: number, noAssert?: boolean): number;
+ writeInt16LE(value: number, offset: number, noAssert?: boolean): number;
+ writeInt16BE(value: number, offset: number, noAssert?: boolean): number;
+ writeInt32LE(value: number, offset: number, noAssert?: boolean): number;
+ writeInt32BE(value: number, offset: number, noAssert?: boolean): number;
+ writeFloatLE(value: number, offset: number, noAssert?: boolean): number;
+ writeFloatBE(value: number, offset: number, noAssert?: boolean): number;
+ writeDoubleLE(value: number, offset: number, noAssert?: boolean): number;
+ writeDoubleBE(value: number, offset: number, noAssert?: boolean): number;
+ fill(value: any, offset?: number, end?: number): this;
+ indexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number;
+ lastIndexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number;
+ includes(value: string | number | Buffer, byteOffset?: number, encoding?: string): boolean;
+
+ /**
+ * Allocates a new buffer containing the given {str}.
+ *
+ * @param str String to store in buffer.
+ * @param encoding encoding to use, optional. Default is 'utf8'
+ */
+ constructor (str: string, encoding?: string);
+ /**
+ * Allocates a new buffer of {size} octets.
+ *
+ * @param size count of octets to allocate.
+ */
+ constructor (size: number);
+ /**
+ * Allocates a new buffer containing the given {array} of octets.
+ *
+ * @param array The octets to store.
+ */
+ constructor (array: Uint8Array);
+ /**
+ * Produces a Buffer backed by the same allocated memory as
+ * the given {ArrayBuffer}.
+ *
+ *
+ * @param arrayBuffer The ArrayBuffer with which to share memory.
+ */
+ constructor (arrayBuffer: ArrayBuffer);
+ /**
+ * Allocates a new buffer containing the given {array} of octets.
+ *
+ * @param array The octets to store.
+ */
+ constructor (array: any[]);
+ /**
+ * Copies the passed {buffer} data onto a new {Buffer} instance.
+ *
+ * @param buffer The buffer to copy.
+ */
+ constructor (buffer: Buffer);
+ prototype: Buffer;
+ /**
+ * Allocates a new Buffer using an {array} of octets.
+ *
+ * @param array
+ */
+ static from(array: any[]): Buffer;
+ /**
+ * When passed a reference to the .buffer property of a TypedArray instance,
+ * the newly created Buffer will share the same allocated memory as the TypedArray.
+ * The optional {byteOffset} and {length} arguments specify a memory range
+ * within the {arrayBuffer} that will be shared by the Buffer.
+ *
+ * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer()
+ * @param byteOffset
+ * @param length
+ */
+ static from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer;
+ /**
+ * Copies the passed {buffer} data onto a new Buffer instance.
+ *
+ * @param buffer
+ */
+ static from(buffer: Buffer | Uint8Array): Buffer;
+ /**
+ * Creates a new Buffer containing the given JavaScript string {str}.
+ * If provided, the {encoding} parameter identifies the character encoding.
+ * If not provided, {encoding} defaults to 'utf8'.
+ *
+ * @param str
+ */
+ static from(str: string, encoding?: string): Buffer;
+ /**
+ * Returns true if {obj} is a Buffer
+ *
+ * @param obj object to test.
+ */
+ static isBuffer(obj: any): obj is Buffer;
+ /**
+ * Returns true if {encoding} is a valid encoding argument.
+ * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex'
+ *
+ * @param encoding string to test.
+ */
+ static isEncoding(encoding: string): boolean;
+ /**
+ * Gives the actual byte length of a string. encoding defaults to 'utf8'.
+ * This is not the same as String.prototype.length since that returns the number of characters in a string.
+ *
+ * @param string string to test.
+ * @param encoding encoding used to evaluate (defaults to 'utf8')
+ */
+ static byteLength(string: string, encoding?: string): number;
+ /**
+ * Returns a buffer which is the result of concatenating all the buffers in the list together.
+ *
+ * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer.
+ * If the list has exactly one item, then the first item of the list is returned.
+ * If the list has more than one item, then a new Buffer is created.
+ *
+ * @param list An array of Buffer objects to concatenate
+ * @param totalLength Total length of the buffers when concatenated.
+ * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly.
+ */
+ static concat(list: Buffer[], totalLength?: number): Buffer;
+ /**
+ * The same as buf1.compare(buf2).
+ */
+ static compare(buf1: Buffer, buf2: Buffer): number;
+ /**
+ * Allocates a new buffer of {size} octets.
+ *
+ * @param size count of octets to allocate.
+ * @param fill if specified, buffer will be initialized by calling buf.fill(fill).
+ * If parameter is omitted, buffer will be filled with zeros.
+ * @param encoding encoding used for call to buf.fill while initializing
+ */
+ static alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer;
+ /**
+ * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents
+ * of the newly created Buffer are unknown and may contain sensitive data.
+ *
+ * @param size count of octets to allocate
+ */
+ static allocUnsafe(size: number): Buffer;
+ /**
+ * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents
+ * of the newly created Buffer are unknown and may contain sensitive data.
+ *
+ * @param size count of octets to allocate
+ */
+ static allocUnsafeSlow(size: number): Buffer;
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/index.js
new file mode 100644
index 000000000..609cf3113
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/index.js
@@ -0,0 +1,1817 @@
+/*!
+ * The buffer module from node.js, for the browser.
+ *
+ * @author Feross Aboukhadijeh
+ * @license MIT
+ */
+/* eslint-disable no-proto */
+
+'use strict'
+
+var base64 = require('base64-js')
+var ieee754 = require('ieee754')
+var customInspectSymbol =
+ (typeof Symbol === 'function' && typeof Symbol['for'] === 'function') // eslint-disable-line dot-notation
+ ? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation
+ : null
+
+exports.Buffer = Buffer
+exports.SlowBuffer = SlowBuffer
+exports.INSPECT_MAX_BYTES = 50
+
+var K_MAX_LENGTH = 0x7fffffff
+exports.kMaxLength = K_MAX_LENGTH
+
+/**
+ * If `Buffer.TYPED_ARRAY_SUPPORT`:
+ * === true Use Uint8Array implementation (fastest)
+ * === false Print warning and recommend using `buffer` v4.x which has an Object
+ * implementation (most compatible, even IE6)
+ *
+ * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
+ * Opera 11.6+, iOS 4.2+.
+ *
+ * We report that the browser does not support typed arrays if the are not subclassable
+ * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`
+ * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support
+ * for __proto__ and has a buggy typed array implementation.
+ */
+Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
+
+if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
+ typeof console.error === 'function') {
+ console.error(
+ 'This browser lacks typed array (Uint8Array) support which is required by ' +
+ '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
+ )
+}
+
+function typedArraySupport () {
+ // Can typed array instances can be augmented?
+ try {
+ var arr = new Uint8Array(1)
+ var proto = { foo: function () { return 42 } }
+ Object.setPrototypeOf(proto, Uint8Array.prototype)
+ Object.setPrototypeOf(arr, proto)
+ return arr.foo() === 42
+ } catch (e) {
+ return false
+ }
+}
+
+Object.defineProperty(Buffer.prototype, 'parent', {
+ enumerable: true,
+ get: function () {
+ if (!Buffer.isBuffer(this)) return undefined
+ return this.buffer
+ }
+})
+
+Object.defineProperty(Buffer.prototype, 'offset', {
+ enumerable: true,
+ get: function () {
+ if (!Buffer.isBuffer(this)) return undefined
+ return this.byteOffset
+ }
+})
+
+function createBuffer (length) {
+ if (length > K_MAX_LENGTH) {
+ throw new RangeError('The value "' + length + '" is invalid for option "size"')
+ }
+ // Return an augmented `Uint8Array` instance
+ var buf = new Uint8Array(length)
+ Object.setPrototypeOf(buf, Buffer.prototype)
+ return buf
+}
+
+/**
+ * The Buffer constructor returns instances of `Uint8Array` that have their
+ * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
+ * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
+ * and the `Uint8Array` methods. Square bracket notation works as expected -- it
+ * returns a single octet.
+ *
+ * The `Uint8Array` prototype remains unmodified.
+ */
+
+function Buffer (arg, encodingOrOffset, length) {
+ // Common case.
+ if (typeof arg === 'number') {
+ if (typeof encodingOrOffset === 'string') {
+ throw new TypeError(
+ 'The "string" argument must be of type string. Received type number'
+ )
+ }
+ return allocUnsafe(arg)
+ }
+ return from(arg, encodingOrOffset, length)
+}
+
+Buffer.poolSize = 8192 // not used by this implementation
+
+function from (value, encodingOrOffset, length) {
+ if (typeof value === 'string') {
+ return fromString(value, encodingOrOffset)
+ }
+
+ if (ArrayBuffer.isView(value)) {
+ return fromArrayView(value)
+ }
+
+ if (value == null) {
+ throw new TypeError(
+ 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
+ 'or Array-like Object. Received type ' + (typeof value)
+ )
+ }
+
+ if (isInstance(value, ArrayBuffer) ||
+ (value && isInstance(value.buffer, ArrayBuffer))) {
+ return fromArrayBuffer(value, encodingOrOffset, length)
+ }
+
+ if (typeof SharedArrayBuffer !== 'undefined' &&
+ (isInstance(value, SharedArrayBuffer) ||
+ (value && isInstance(value.buffer, SharedArrayBuffer)))) {
+ return fromArrayBuffer(value, encodingOrOffset, length)
+ }
+
+ if (typeof value === 'number') {
+ throw new TypeError(
+ 'The "value" argument must not be of type number. Received type number'
+ )
+ }
+
+ var valueOf = value.valueOf && value.valueOf()
+ if (valueOf != null && valueOf !== value) {
+ return Buffer.from(valueOf, encodingOrOffset, length)
+ }
+
+ var b = fromObject(value)
+ if (b) return b
+
+ if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
+ typeof value[Symbol.toPrimitive] === 'function') {
+ return Buffer.from(
+ value[Symbol.toPrimitive]('string'), encodingOrOffset, length
+ )
+ }
+
+ throw new TypeError(
+ 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
+ 'or Array-like Object. Received type ' + (typeof value)
+ )
+}
+
+/**
+ * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
+ * if value is a number.
+ * Buffer.from(str[, encoding])
+ * Buffer.from(array)
+ * Buffer.from(buffer)
+ * Buffer.from(arrayBuffer[, byteOffset[, length]])
+ **/
+Buffer.from = function (value, encodingOrOffset, length) {
+ return from(value, encodingOrOffset, length)
+}
+
+// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:
+// https://github.com/feross/buffer/pull/148
+Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype)
+Object.setPrototypeOf(Buffer, Uint8Array)
+
+function assertSize (size) {
+ if (typeof size !== 'number') {
+ throw new TypeError('"size" argument must be of type number')
+ } else if (size < 0) {
+ throw new RangeError('The value "' + size + '" is invalid for option "size"')
+ }
+}
+
+function alloc (size, fill, encoding) {
+ assertSize(size)
+ if (size <= 0) {
+ return createBuffer(size)
+ }
+ if (fill !== undefined) {
+ // Only pay attention to encoding if it's a string. This
+ // prevents accidentally sending in a number that would
+ // be interpreted as a start offset.
+ return typeof encoding === 'string'
+ ? createBuffer(size).fill(fill, encoding)
+ : createBuffer(size).fill(fill)
+ }
+ return createBuffer(size)
+}
+
+/**
+ * Creates a new filled Buffer instance.
+ * alloc(size[, fill[, encoding]])
+ **/
+Buffer.alloc = function (size, fill, encoding) {
+ return alloc(size, fill, encoding)
+}
+
+function allocUnsafe (size) {
+ assertSize(size)
+ return createBuffer(size < 0 ? 0 : checked(size) | 0)
+}
+
+/**
+ * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
+ * */
+Buffer.allocUnsafe = function (size) {
+ return allocUnsafe(size)
+}
+/**
+ * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
+ */
+Buffer.allocUnsafeSlow = function (size) {
+ return allocUnsafe(size)
+}
+
+function fromString (string, encoding) {
+ if (typeof encoding !== 'string' || encoding === '') {
+ encoding = 'utf8'
+ }
+
+ if (!Buffer.isEncoding(encoding)) {
+ throw new TypeError('Unknown encoding: ' + encoding)
+ }
+
+ var length = byteLength(string, encoding) | 0
+ var buf = createBuffer(length)
+
+ var actual = buf.write(string, encoding)
+
+ if (actual !== length) {
+ // Writing a hex string, for example, that contains invalid characters will
+ // cause everything after the first invalid character to be ignored. (e.g.
+ // 'abxxcd' will be treated as 'ab')
+ buf = buf.slice(0, actual)
+ }
+
+ return buf
+}
+
+function fromArrayLike (array) {
+ var length = array.length < 0 ? 0 : checked(array.length) | 0
+ var buf = createBuffer(length)
+ for (var i = 0; i < length; i += 1) {
+ buf[i] = array[i] & 255
+ }
+ return buf
+}
+
+function fromArrayView (arrayView) {
+ if (isInstance(arrayView, Uint8Array)) {
+ var copy = new Uint8Array(arrayView)
+ return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength)
+ }
+ return fromArrayLike(arrayView)
+}
+
+function fromArrayBuffer (array, byteOffset, length) {
+ if (byteOffset < 0 || array.byteLength < byteOffset) {
+ throw new RangeError('"offset" is outside of buffer bounds')
+ }
+
+ if (array.byteLength < byteOffset + (length || 0)) {
+ throw new RangeError('"length" is outside of buffer bounds')
+ }
+
+ var buf
+ if (byteOffset === undefined && length === undefined) {
+ buf = new Uint8Array(array)
+ } else if (length === undefined) {
+ buf = new Uint8Array(array, byteOffset)
+ } else {
+ buf = new Uint8Array(array, byteOffset, length)
+ }
+
+ // Return an augmented `Uint8Array` instance
+ Object.setPrototypeOf(buf, Buffer.prototype)
+
+ return buf
+}
+
+function fromObject (obj) {
+ if (Buffer.isBuffer(obj)) {
+ var len = checked(obj.length) | 0
+ var buf = createBuffer(len)
+
+ if (buf.length === 0) {
+ return buf
+ }
+
+ obj.copy(buf, 0, 0, len)
+ return buf
+ }
+
+ if (obj.length !== undefined) {
+ if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
+ return createBuffer(0)
+ }
+ return fromArrayLike(obj)
+ }
+
+ if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
+ return fromArrayLike(obj.data)
+ }
+}
+
+function checked (length) {
+ // Note: cannot use `length < K_MAX_LENGTH` here because that fails when
+ // length is NaN (which is otherwise coerced to zero.)
+ if (length >= K_MAX_LENGTH) {
+ throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
+ 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')
+ }
+ return length | 0
+}
+
+function SlowBuffer (length) {
+ if (+length != length) { // eslint-disable-line eqeqeq
+ length = 0
+ }
+ return Buffer.alloc(+length)
+}
+
+Buffer.isBuffer = function isBuffer (b) {
+ return b != null && b._isBuffer === true &&
+ b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false
+}
+
+Buffer.compare = function compare (a, b) {
+ if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)
+ if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)
+ if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
+ throw new TypeError(
+ 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'
+ )
+ }
+
+ if (a === b) return 0
+
+ var x = a.length
+ var y = b.length
+
+ for (var i = 0, len = Math.min(x, y); i < len; ++i) {
+ if (a[i] !== b[i]) {
+ x = a[i]
+ y = b[i]
+ break
+ }
+ }
+
+ if (x < y) return -1
+ if (y < x) return 1
+ return 0
+}
+
+Buffer.isEncoding = function isEncoding (encoding) {
+ switch (String(encoding).toLowerCase()) {
+ case 'hex':
+ case 'utf8':
+ case 'utf-8':
+ case 'ascii':
+ case 'latin1':
+ case 'binary':
+ case 'base64':
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return true
+ default:
+ return false
+ }
+}
+
+Buffer.concat = function concat (list, length) {
+ if (!Array.isArray(list)) {
+ throw new TypeError('"list" argument must be an Array of Buffers')
+ }
+
+ if (list.length === 0) {
+ return Buffer.alloc(0)
+ }
+
+ var i
+ if (length === undefined) {
+ length = 0
+ for (i = 0; i < list.length; ++i) {
+ length += list[i].length
+ }
+ }
+
+ var buffer = Buffer.allocUnsafe(length)
+ var pos = 0
+ for (i = 0; i < list.length; ++i) {
+ var buf = list[i]
+ if (isInstance(buf, Uint8Array)) {
+ if (pos + buf.length > buffer.length) {
+ Buffer.from(buf).copy(buffer, pos)
+ } else {
+ Uint8Array.prototype.set.call(
+ buffer,
+ buf,
+ pos
+ )
+ }
+ } else if (!Buffer.isBuffer(buf)) {
+ throw new TypeError('"list" argument must be an Array of Buffers')
+ } else {
+ buf.copy(buffer, pos)
+ }
+ pos += buf.length
+ }
+ return buffer
+}
+
+function byteLength (string, encoding) {
+ if (Buffer.isBuffer(string)) {
+ return string.length
+ }
+ if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
+ return string.byteLength
+ }
+ if (typeof string !== 'string') {
+ throw new TypeError(
+ 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
+ 'Received type ' + typeof string
+ )
+ }
+
+ var len = string.length
+ var mustMatch = (arguments.length > 2 && arguments[2] === true)
+ if (!mustMatch && len === 0) return 0
+
+ // Use a for loop to avoid recursion
+ var loweredCase = false
+ for (;;) {
+ switch (encoding) {
+ case 'ascii':
+ case 'latin1':
+ case 'binary':
+ return len
+ case 'utf8':
+ case 'utf-8':
+ return utf8ToBytes(string).length
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return len * 2
+ case 'hex':
+ return len >>> 1
+ case 'base64':
+ return base64ToBytes(string).length
+ default:
+ if (loweredCase) {
+ return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8
+ }
+ encoding = ('' + encoding).toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+Buffer.byteLength = byteLength
+
+function slowToString (encoding, start, end) {
+ var loweredCase = false
+
+ // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
+ // property of a typed array.
+
+ // This behaves neither like String nor Uint8Array in that we set start/end
+ // to their upper/lower bounds if the value passed is out of range.
+ // undefined is handled specially as per ECMA-262 6th Edition,
+ // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
+ if (start === undefined || start < 0) {
+ start = 0
+ }
+ // Return early if start > this.length. Done here to prevent potential uint32
+ // coercion fail below.
+ if (start > this.length) {
+ return ''
+ }
+
+ if (end === undefined || end > this.length) {
+ end = this.length
+ }
+
+ if (end <= 0) {
+ return ''
+ }
+
+ // Force coercion to uint32. This will also coerce falsey/NaN values to 0.
+ end >>>= 0
+ start >>>= 0
+
+ if (end <= start) {
+ return ''
+ }
+
+ if (!encoding) encoding = 'utf8'
+
+ while (true) {
+ switch (encoding) {
+ case 'hex':
+ return hexSlice(this, start, end)
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8Slice(this, start, end)
+
+ case 'ascii':
+ return asciiSlice(this, start, end)
+
+ case 'latin1':
+ case 'binary':
+ return latin1Slice(this, start, end)
+
+ case 'base64':
+ return base64Slice(this, start, end)
+
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return utf16leSlice(this, start, end)
+
+ default:
+ if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
+ encoding = (encoding + '').toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+
+// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)
+// to detect a Buffer instance. It's not possible to use `instanceof Buffer`
+// reliably in a browserify context because there could be multiple different
+// copies of the 'buffer' package in use. This method works even for Buffer
+// instances that were created from another copy of the `buffer` package.
+// See: https://github.com/feross/buffer/issues/154
+Buffer.prototype._isBuffer = true
+
+function swap (b, n, m) {
+ var i = b[n]
+ b[n] = b[m]
+ b[m] = i
+}
+
+Buffer.prototype.swap16 = function swap16 () {
+ var len = this.length
+ if (len % 2 !== 0) {
+ throw new RangeError('Buffer size must be a multiple of 16-bits')
+ }
+ for (var i = 0; i < len; i += 2) {
+ swap(this, i, i + 1)
+ }
+ return this
+}
+
+Buffer.prototype.swap32 = function swap32 () {
+ var len = this.length
+ if (len % 4 !== 0) {
+ throw new RangeError('Buffer size must be a multiple of 32-bits')
+ }
+ for (var i = 0; i < len; i += 4) {
+ swap(this, i, i + 3)
+ swap(this, i + 1, i + 2)
+ }
+ return this
+}
+
+Buffer.prototype.swap64 = function swap64 () {
+ var len = this.length
+ if (len % 8 !== 0) {
+ throw new RangeError('Buffer size must be a multiple of 64-bits')
+ }
+ for (var i = 0; i < len; i += 8) {
+ swap(this, i, i + 7)
+ swap(this, i + 1, i + 6)
+ swap(this, i + 2, i + 5)
+ swap(this, i + 3, i + 4)
+ }
+ return this
+}
+
+Buffer.prototype.toString = function toString () {
+ var length = this.length
+ if (length === 0) return ''
+ if (arguments.length === 0) return utf8Slice(this, 0, length)
+ return slowToString.apply(this, arguments)
+}
+
+Buffer.prototype.toLocaleString = Buffer.prototype.toString
+
+Buffer.prototype.equals = function equals (b) {
+ if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
+ if (this === b) return true
+ return Buffer.compare(this, b) === 0
+}
+
+Buffer.prototype.inspect = function inspect () {
+ var str = ''
+ var max = exports.INSPECT_MAX_BYTES
+ str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()
+ if (this.length > max) str += ' ... '
+ return ''
+}
+if (customInspectSymbol) {
+ Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect
+}
+
+Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
+ if (isInstance(target, Uint8Array)) {
+ target = Buffer.from(target, target.offset, target.byteLength)
+ }
+ if (!Buffer.isBuffer(target)) {
+ throw new TypeError(
+ 'The "target" argument must be one of type Buffer or Uint8Array. ' +
+ 'Received type ' + (typeof target)
+ )
+ }
+
+ if (start === undefined) {
+ start = 0
+ }
+ if (end === undefined) {
+ end = target ? target.length : 0
+ }
+ if (thisStart === undefined) {
+ thisStart = 0
+ }
+ if (thisEnd === undefined) {
+ thisEnd = this.length
+ }
+
+ if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
+ throw new RangeError('out of range index')
+ }
+
+ if (thisStart >= thisEnd && start >= end) {
+ return 0
+ }
+ if (thisStart >= thisEnd) {
+ return -1
+ }
+ if (start >= end) {
+ return 1
+ }
+
+ start >>>= 0
+ end >>>= 0
+ thisStart >>>= 0
+ thisEnd >>>= 0
+
+ if (this === target) return 0
+
+ var x = thisEnd - thisStart
+ var y = end - start
+ var len = Math.min(x, y)
+
+ var thisCopy = this.slice(thisStart, thisEnd)
+ var targetCopy = target.slice(start, end)
+
+ for (var i = 0; i < len; ++i) {
+ if (thisCopy[i] !== targetCopy[i]) {
+ x = thisCopy[i]
+ y = targetCopy[i]
+ break
+ }
+ }
+
+ if (x < y) return -1
+ if (y < x) return 1
+ return 0
+}
+
+// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
+// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
+//
+// Arguments:
+// - buffer - a Buffer to search
+// - val - a string, Buffer, or number
+// - byteOffset - an index into `buffer`; will be clamped to an int32
+// - encoding - an optional encoding, relevant is val is a string
+// - dir - true for indexOf, false for lastIndexOf
+function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
+ // Empty buffer means no match
+ if (buffer.length === 0) return -1
+
+ // Normalize byteOffset
+ if (typeof byteOffset === 'string') {
+ encoding = byteOffset
+ byteOffset = 0
+ } else if (byteOffset > 0x7fffffff) {
+ byteOffset = 0x7fffffff
+ } else if (byteOffset < -0x80000000) {
+ byteOffset = -0x80000000
+ }
+ byteOffset = +byteOffset // Coerce to Number.
+ if (numberIsNaN(byteOffset)) {
+ // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
+ byteOffset = dir ? 0 : (buffer.length - 1)
+ }
+
+ // Normalize byteOffset: negative offsets start from the end of the buffer
+ if (byteOffset < 0) byteOffset = buffer.length + byteOffset
+ if (byteOffset >= buffer.length) {
+ if (dir) return -1
+ else byteOffset = buffer.length - 1
+ } else if (byteOffset < 0) {
+ if (dir) byteOffset = 0
+ else return -1
+ }
+
+ // Normalize val
+ if (typeof val === 'string') {
+ val = Buffer.from(val, encoding)
+ }
+
+ // Finally, search either indexOf (if dir is true) or lastIndexOf
+ if (Buffer.isBuffer(val)) {
+ // Special case: looking for empty string/buffer always fails
+ if (val.length === 0) {
+ return -1
+ }
+ return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
+ } else if (typeof val === 'number') {
+ val = val & 0xFF // Search for a byte value [0-255]
+ if (typeof Uint8Array.prototype.indexOf === 'function') {
+ if (dir) {
+ return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
+ } else {
+ return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
+ }
+ }
+ return arrayIndexOf(buffer, [val], byteOffset, encoding, dir)
+ }
+
+ throw new TypeError('val must be string, number or Buffer')
+}
+
+function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
+ var indexSize = 1
+ var arrLength = arr.length
+ var valLength = val.length
+
+ if (encoding !== undefined) {
+ encoding = String(encoding).toLowerCase()
+ if (encoding === 'ucs2' || encoding === 'ucs-2' ||
+ encoding === 'utf16le' || encoding === 'utf-16le') {
+ if (arr.length < 2 || val.length < 2) {
+ return -1
+ }
+ indexSize = 2
+ arrLength /= 2
+ valLength /= 2
+ byteOffset /= 2
+ }
+ }
+
+ function read (buf, i) {
+ if (indexSize === 1) {
+ return buf[i]
+ } else {
+ return buf.readUInt16BE(i * indexSize)
+ }
+ }
+
+ var i
+ if (dir) {
+ var foundIndex = -1
+ for (i = byteOffset; i < arrLength; i++) {
+ if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
+ if (foundIndex === -1) foundIndex = i
+ if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
+ } else {
+ if (foundIndex !== -1) i -= i - foundIndex
+ foundIndex = -1
+ }
+ }
+ } else {
+ if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
+ for (i = byteOffset; i >= 0; i--) {
+ var found = true
+ for (var j = 0; j < valLength; j++) {
+ if (read(arr, i + j) !== read(val, j)) {
+ found = false
+ break
+ }
+ }
+ if (found) return i
+ }
+ }
+
+ return -1
+}
+
+Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
+ return this.indexOf(val, byteOffset, encoding) !== -1
+}
+
+Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
+ return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
+}
+
+Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
+ return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
+}
+
+function hexWrite (buf, string, offset, length) {
+ offset = Number(offset) || 0
+ var remaining = buf.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+
+ var strLen = string.length
+
+ if (length > strLen / 2) {
+ length = strLen / 2
+ }
+ for (var i = 0; i < length; ++i) {
+ var parsed = parseInt(string.substr(i * 2, 2), 16)
+ if (numberIsNaN(parsed)) return i
+ buf[offset + i] = parsed
+ }
+ return i
+}
+
+function utf8Write (buf, string, offset, length) {
+ return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
+}
+
+function asciiWrite (buf, string, offset, length) {
+ return blitBuffer(asciiToBytes(string), buf, offset, length)
+}
+
+function base64Write (buf, string, offset, length) {
+ return blitBuffer(base64ToBytes(string), buf, offset, length)
+}
+
+function ucs2Write (buf, string, offset, length) {
+ return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
+}
+
+Buffer.prototype.write = function write (string, offset, length, encoding) {
+ // Buffer#write(string)
+ if (offset === undefined) {
+ encoding = 'utf8'
+ length = this.length
+ offset = 0
+ // Buffer#write(string, encoding)
+ } else if (length === undefined && typeof offset === 'string') {
+ encoding = offset
+ length = this.length
+ offset = 0
+ // Buffer#write(string, offset[, length][, encoding])
+ } else if (isFinite(offset)) {
+ offset = offset >>> 0
+ if (isFinite(length)) {
+ length = length >>> 0
+ if (encoding === undefined) encoding = 'utf8'
+ } else {
+ encoding = length
+ length = undefined
+ }
+ } else {
+ throw new Error(
+ 'Buffer.write(string, encoding, offset[, length]) is no longer supported'
+ )
+ }
+
+ var remaining = this.length - offset
+ if (length === undefined || length > remaining) length = remaining
+
+ if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
+ throw new RangeError('Attempt to write outside buffer bounds')
+ }
+
+ if (!encoding) encoding = 'utf8'
+
+ var loweredCase = false
+ for (;;) {
+ switch (encoding) {
+ case 'hex':
+ return hexWrite(this, string, offset, length)
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8Write(this, string, offset, length)
+
+ case 'ascii':
+ case 'latin1':
+ case 'binary':
+ return asciiWrite(this, string, offset, length)
+
+ case 'base64':
+ // Warning: maxLength not taken into account in base64Write
+ return base64Write(this, string, offset, length)
+
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return ucs2Write(this, string, offset, length)
+
+ default:
+ if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
+ encoding = ('' + encoding).toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+
+Buffer.prototype.toJSON = function toJSON () {
+ return {
+ type: 'Buffer',
+ data: Array.prototype.slice.call(this._arr || this, 0)
+ }
+}
+
+function base64Slice (buf, start, end) {
+ if (start === 0 && end === buf.length) {
+ return base64.fromByteArray(buf)
+ } else {
+ return base64.fromByteArray(buf.slice(start, end))
+ }
+}
+
+function utf8Slice (buf, start, end) {
+ end = Math.min(buf.length, end)
+ var res = []
+
+ var i = start
+ while (i < end) {
+ var firstByte = buf[i]
+ var codePoint = null
+ var bytesPerSequence = (firstByte > 0xEF)
+ ? 4
+ : (firstByte > 0xDF)
+ ? 3
+ : (firstByte > 0xBF)
+ ? 2
+ : 1
+
+ if (i + bytesPerSequence <= end) {
+ var secondByte, thirdByte, fourthByte, tempCodePoint
+
+ switch (bytesPerSequence) {
+ case 1:
+ if (firstByte < 0x80) {
+ codePoint = firstByte
+ }
+ break
+ case 2:
+ secondByte = buf[i + 1]
+ if ((secondByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
+ if (tempCodePoint > 0x7F) {
+ codePoint = tempCodePoint
+ }
+ }
+ break
+ case 3:
+ secondByte = buf[i + 1]
+ thirdByte = buf[i + 2]
+ if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
+ if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
+ codePoint = tempCodePoint
+ }
+ }
+ break
+ case 4:
+ secondByte = buf[i + 1]
+ thirdByte = buf[i + 2]
+ fourthByte = buf[i + 3]
+ if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
+ if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
+ codePoint = tempCodePoint
+ }
+ }
+ }
+ }
+
+ if (codePoint === null) {
+ // we did not generate a valid codePoint so insert a
+ // replacement char (U+FFFD) and advance only 1 byte
+ codePoint = 0xFFFD
+ bytesPerSequence = 1
+ } else if (codePoint > 0xFFFF) {
+ // encode to utf16 (surrogate pair dance)
+ codePoint -= 0x10000
+ res.push(codePoint >>> 10 & 0x3FF | 0xD800)
+ codePoint = 0xDC00 | codePoint & 0x3FF
+ }
+
+ res.push(codePoint)
+ i += bytesPerSequence
+ }
+
+ return decodeCodePointsArray(res)
+}
+
+// Based on http://stackoverflow.com/a/22747272/680742, the browser with
+// the lowest limit is Chrome, with 0x10000 args.
+// We go 1 magnitude less, for safety
+var MAX_ARGUMENTS_LENGTH = 0x1000
+
+function decodeCodePointsArray (codePoints) {
+ var len = codePoints.length
+ if (len <= MAX_ARGUMENTS_LENGTH) {
+ return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
+ }
+
+ // Decode in chunks to avoid "call stack size exceeded".
+ var res = ''
+ var i = 0
+ while (i < len) {
+ res += String.fromCharCode.apply(
+ String,
+ codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
+ )
+ }
+ return res
+}
+
+function asciiSlice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; ++i) {
+ ret += String.fromCharCode(buf[i] & 0x7F)
+ }
+ return ret
+}
+
+function latin1Slice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; ++i) {
+ ret += String.fromCharCode(buf[i])
+ }
+ return ret
+}
+
+function hexSlice (buf, start, end) {
+ var len = buf.length
+
+ if (!start || start < 0) start = 0
+ if (!end || end < 0 || end > len) end = len
+
+ var out = ''
+ for (var i = start; i < end; ++i) {
+ out += hexSliceLookupTable[buf[i]]
+ }
+ return out
+}
+
+function utf16leSlice (buf, start, end) {
+ var bytes = buf.slice(start, end)
+ var res = ''
+ // If bytes.length is odd, the last 8 bits must be ignored (same as node.js)
+ for (var i = 0; i < bytes.length - 1; i += 2) {
+ res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256))
+ }
+ return res
+}
+
+Buffer.prototype.slice = function slice (start, end) {
+ var len = this.length
+ start = ~~start
+ end = end === undefined ? len : ~~end
+
+ if (start < 0) {
+ start += len
+ if (start < 0) start = 0
+ } else if (start > len) {
+ start = len
+ }
+
+ if (end < 0) {
+ end += len
+ if (end < 0) end = 0
+ } else if (end > len) {
+ end = len
+ }
+
+ if (end < start) end = start
+
+ var newBuf = this.subarray(start, end)
+ // Return an augmented `Uint8Array` instance
+ Object.setPrototypeOf(newBuf, Buffer.prototype)
+
+ return newBuf
+}
+
+/*
+ * Need to make sure that buffer isn't trying to write out of bounds.
+ */
+function checkOffset (offset, ext, length) {
+ if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
+ if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
+}
+
+Buffer.prototype.readUintLE =
+Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUintBE =
+Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) {
+ checkOffset(offset, byteLength, this.length)
+ }
+
+ var val = this[offset + --byteLength]
+ var mul = 1
+ while (byteLength > 0 && (mul *= 0x100)) {
+ val += this[offset + --byteLength] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUint8 =
+Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ return this[offset]
+}
+
+Buffer.prototype.readUint16LE =
+Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return this[offset] | (this[offset + 1] << 8)
+}
+
+Buffer.prototype.readUint16BE =
+Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return (this[offset] << 8) | this[offset + 1]
+}
+
+Buffer.prototype.readUint32LE =
+Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return ((this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16)) +
+ (this[offset + 3] * 0x1000000)
+}
+
+Buffer.prototype.readUint32BE =
+Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] * 0x1000000) +
+ ((this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ this[offset + 3])
+}
+
+Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var i = byteLength
+ var mul = 1
+ var val = this[offset + --i]
+ while (i > 0 && (mul *= 0x100)) {
+ val += this[offset + --i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ if (!(this[offset] & 0x80)) return (this[offset])
+ return ((0xff - this[offset] + 1) * -1)
+}
+
+Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset] | (this[offset + 1] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset + 1] | (this[offset] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16) |
+ (this[offset + 3] << 24)
+}
+
+Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] << 24) |
+ (this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ (this[offset + 3])
+}
+
+Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, true, 23, 4)
+}
+
+Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, false, 23, 4)
+}
+
+Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, true, 52, 8)
+}
+
+Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, false, 52, 8)
+}
+
+function checkInt (buf, value, offset, ext, max, min) {
+ if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
+ if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
+ if (offset + ext > buf.length) throw new RangeError('Index out of range')
+}
+
+Buffer.prototype.writeUintLE =
+Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) {
+ var maxBytes = Math.pow(2, 8 * byteLength) - 1
+ checkInt(this, value, offset, byteLength, maxBytes, 0)
+ }
+
+ var mul = 1
+ var i = 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUintBE =
+Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) {
+ var maxBytes = Math.pow(2, 8 * byteLength) - 1
+ checkInt(this, value, offset, byteLength, maxBytes, 0)
+ }
+
+ var i = byteLength - 1
+ var mul = 1
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUint8 =
+Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
+ this[offset] = (value & 0xff)
+ return offset + 1
+}
+
+Buffer.prototype.writeUint16LE =
+Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ return offset + 2
+}
+
+Buffer.prototype.writeUint16BE =
+Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ this[offset] = (value >>> 8)
+ this[offset + 1] = (value & 0xff)
+ return offset + 2
+}
+
+Buffer.prototype.writeUint32LE =
+Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ this[offset + 3] = (value >>> 24)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 1] = (value >>> 8)
+ this[offset] = (value & 0xff)
+ return offset + 4
+}
+
+Buffer.prototype.writeUint32BE =
+Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = (value & 0xff)
+ return offset + 4
+}
+
+Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ var limit = Math.pow(2, (8 * byteLength) - 1)
+
+ checkInt(this, value, offset, byteLength, limit - 1, -limit)
+ }
+
+ var i = 0
+ var mul = 1
+ var sub = 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
+ sub = 1
+ }
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ var limit = Math.pow(2, (8 * byteLength) - 1)
+
+ checkInt(this, value, offset, byteLength, limit - 1, -limit)
+ }
+
+ var i = byteLength - 1
+ var mul = 1
+ var sub = 0
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
+ sub = 1
+ }
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
+ if (value < 0) value = 0xff + value + 1
+ this[offset] = (value & 0xff)
+ return offset + 1
+}
+
+Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ return offset + 2
+}
+
+Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ this[offset] = (value >>> 8)
+ this[offset + 1] = (value & 0xff)
+ return offset + 2
+}
+
+Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 3] = (value >>> 24)
+ return offset + 4
+}
+
+Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ if (value < 0) value = 0xffffffff + value + 1
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = (value & 0xff)
+ return offset + 4
+}
+
+function checkIEEE754 (buf, value, offset, ext, max, min) {
+ if (offset + ext > buf.length) throw new RangeError('Index out of range')
+ if (offset < 0) throw new RangeError('Index out of range')
+}
+
+function writeFloat (buf, value, offset, littleEndian, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 23, 4)
+ return offset + 4
+}
+
+Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, false, noAssert)
+}
+
+function writeDouble (buf, value, offset, littleEndian, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 52, 8)
+ return offset + 8
+}
+
+Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, false, noAssert)
+}
+
+// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
+Buffer.prototype.copy = function copy (target, targetStart, start, end) {
+ if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer')
+ if (!start) start = 0
+ if (!end && end !== 0) end = this.length
+ if (targetStart >= target.length) targetStart = target.length
+ if (!targetStart) targetStart = 0
+ if (end > 0 && end < start) end = start
+
+ // Copy 0 bytes; we're done
+ if (end === start) return 0
+ if (target.length === 0 || this.length === 0) return 0
+
+ // Fatal error conditions
+ if (targetStart < 0) {
+ throw new RangeError('targetStart out of bounds')
+ }
+ if (start < 0 || start >= this.length) throw new RangeError('Index out of range')
+ if (end < 0) throw new RangeError('sourceEnd out of bounds')
+
+ // Are we oob?
+ if (end > this.length) end = this.length
+ if (target.length - targetStart < end - start) {
+ end = target.length - targetStart + start
+ }
+
+ var len = end - start
+
+ if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {
+ // Use built-in when available, missing from IE11
+ this.copyWithin(targetStart, start, end)
+ } else {
+ Uint8Array.prototype.set.call(
+ target,
+ this.subarray(start, end),
+ targetStart
+ )
+ }
+
+ return len
+}
+
+// Usage:
+// buffer.fill(number[, offset[, end]])
+// buffer.fill(buffer[, offset[, end]])
+// buffer.fill(string[, offset[, end]][, encoding])
+Buffer.prototype.fill = function fill (val, start, end, encoding) {
+ // Handle string cases:
+ if (typeof val === 'string') {
+ if (typeof start === 'string') {
+ encoding = start
+ start = 0
+ end = this.length
+ } else if (typeof end === 'string') {
+ encoding = end
+ end = this.length
+ }
+ if (encoding !== undefined && typeof encoding !== 'string') {
+ throw new TypeError('encoding must be a string')
+ }
+ if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
+ throw new TypeError('Unknown encoding: ' + encoding)
+ }
+ if (val.length === 1) {
+ var code = val.charCodeAt(0)
+ if ((encoding === 'utf8' && code < 128) ||
+ encoding === 'latin1') {
+ // Fast path: If `val` fits into a single byte, use that numeric value.
+ val = code
+ }
+ }
+ } else if (typeof val === 'number') {
+ val = val & 255
+ } else if (typeof val === 'boolean') {
+ val = Number(val)
+ }
+
+ // Invalid ranges are not set to a default, so can range check early.
+ if (start < 0 || this.length < start || this.length < end) {
+ throw new RangeError('Out of range index')
+ }
+
+ if (end <= start) {
+ return this
+ }
+
+ start = start >>> 0
+ end = end === undefined ? this.length : end >>> 0
+
+ if (!val) val = 0
+
+ var i
+ if (typeof val === 'number') {
+ for (i = start; i < end; ++i) {
+ this[i] = val
+ }
+ } else {
+ var bytes = Buffer.isBuffer(val)
+ ? val
+ : Buffer.from(val, encoding)
+ var len = bytes.length
+ if (len === 0) {
+ throw new TypeError('The value "' + val +
+ '" is invalid for argument "value"')
+ }
+ for (i = 0; i < end - start; ++i) {
+ this[i + start] = bytes[i % len]
+ }
+ }
+
+ return this
+}
+
+// HELPER FUNCTIONS
+// ================
+
+var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g
+
+function base64clean (str) {
+ // Node takes equal signs as end of the Base64 encoding
+ str = str.split('=')[0]
+ // Node strips out invalid characters like \n and \t from the string, base64-js does not
+ str = str.trim().replace(INVALID_BASE64_RE, '')
+ // Node converts strings with length < 2 to ''
+ if (str.length < 2) return ''
+ // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
+ while (str.length % 4 !== 0) {
+ str = str + '='
+ }
+ return str
+}
+
+function utf8ToBytes (string, units) {
+ units = units || Infinity
+ var codePoint
+ var length = string.length
+ var leadSurrogate = null
+ var bytes = []
+
+ for (var i = 0; i < length; ++i) {
+ codePoint = string.charCodeAt(i)
+
+ // is surrogate component
+ if (codePoint > 0xD7FF && codePoint < 0xE000) {
+ // last char was a lead
+ if (!leadSurrogate) {
+ // no lead yet
+ if (codePoint > 0xDBFF) {
+ // unexpected trail
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ } else if (i + 1 === length) {
+ // unpaired lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ }
+
+ // valid lead
+ leadSurrogate = codePoint
+
+ continue
+ }
+
+ // 2 leads in a row
+ if (codePoint < 0xDC00) {
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ leadSurrogate = codePoint
+ continue
+ }
+
+ // valid surrogate pair
+ codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
+ } else if (leadSurrogate) {
+ // valid bmp char, but last char was a lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ }
+
+ leadSurrogate = null
+
+ // encode utf8
+ if (codePoint < 0x80) {
+ if ((units -= 1) < 0) break
+ bytes.push(codePoint)
+ } else if (codePoint < 0x800) {
+ if ((units -= 2) < 0) break
+ bytes.push(
+ codePoint >> 0x6 | 0xC0,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x10000) {
+ if ((units -= 3) < 0) break
+ bytes.push(
+ codePoint >> 0xC | 0xE0,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x110000) {
+ if ((units -= 4) < 0) break
+ bytes.push(
+ codePoint >> 0x12 | 0xF0,
+ codePoint >> 0xC & 0x3F | 0x80,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else {
+ throw new Error('Invalid code point')
+ }
+ }
+
+ return bytes
+}
+
+function asciiToBytes (str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; ++i) {
+ // Node's code seems to be doing this and not & 0x7F..
+ byteArray.push(str.charCodeAt(i) & 0xFF)
+ }
+ return byteArray
+}
+
+function utf16leToBytes (str, units) {
+ var c, hi, lo
+ var byteArray = []
+ for (var i = 0; i < str.length; ++i) {
+ if ((units -= 2) < 0) break
+
+ c = str.charCodeAt(i)
+ hi = c >> 8
+ lo = c % 256
+ byteArray.push(lo)
+ byteArray.push(hi)
+ }
+
+ return byteArray
+}
+
+function base64ToBytes (str) {
+ return base64.toByteArray(base64clean(str))
+}
+
+function blitBuffer (src, dst, offset, length) {
+ for (var i = 0; i < length; ++i) {
+ if ((i + offset >= dst.length) || (i >= src.length)) break
+ dst[i + offset] = src[i]
+ }
+ return i
+}
+
+// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass
+// the `instanceof` check but they should be treated as of that type.
+// See: https://github.com/feross/buffer/issues/166
+function isInstance (obj, type) {
+ return obj instanceof type ||
+ (obj != null && obj.constructor != null && obj.constructor.name != null &&
+ obj.constructor.name === type.name)
+}
+function numberIsNaN (obj) {
+ // For IE11 support
+ return obj !== obj // eslint-disable-line no-self-compare
+}
+
+// Create lookup table for `toString('hex')`
+// See: https://github.com/feross/buffer/issues/219
+var hexSliceLookupTable = (function () {
+ var alphabet = '0123456789abcdef'
+ var table = new Array(256)
+ for (var i = 0; i < 16; ++i) {
+ var i16 = i * 16
+ for (var j = 0; j < 16; ++j) {
+ table[i16 + j] = alphabet[i] + alphabet[j]
+ }
+ }
+ return table
+})()
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/package.json
new file mode 100644
index 000000000..3b1b4986f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/buffer/package.json
@@ -0,0 +1,96 @@
+{
+ "name": "buffer",
+ "description": "Node.js Buffer API, for the browser",
+ "version": "5.7.1",
+ "author": {
+ "name": "Feross Aboukhadijeh",
+ "email": "feross@feross.org",
+ "url": "https://feross.org"
+ },
+ "bugs": {
+ "url": "https://github.com/feross/buffer/issues"
+ },
+ "contributors": [
+ "Romain Beauxis ",
+ "James Halliday "
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ },
+ "devDependencies": {
+ "airtap": "^3.0.0",
+ "benchmark": "^2.1.4",
+ "browserify": "^17.0.0",
+ "concat-stream": "^2.0.0",
+ "hyperquest": "^2.1.3",
+ "is-buffer": "^2.0.4",
+ "is-nan": "^1.3.0",
+ "split": "^1.0.1",
+ "standard": "*",
+ "tape": "^5.0.1",
+ "through2": "^4.0.2",
+ "uglify-js": "^3.11.3"
+ },
+ "homepage": "https://github.com/feross/buffer",
+ "jspm": {
+ "map": {
+ "./index.js": {
+ "node": "@node/buffer"
+ }
+ }
+ },
+ "keywords": [
+ "arraybuffer",
+ "browser",
+ "browserify",
+ "buffer",
+ "compatible",
+ "dataview",
+ "uint8array"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "types": "index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/feross/buffer.git"
+ },
+ "scripts": {
+ "perf": "browserify --debug perf/bracket-notation.js > perf/bundle.js && open perf/index.html",
+ "perf-node": "node perf/bracket-notation.js && node perf/concat.js && node perf/copy-big.js && node perf/copy.js && node perf/new-big.js && node perf/new.js && node perf/readDoubleBE.js && node perf/readFloatBE.js && node perf/readUInt32LE.js && node perf/slice.js && node perf/writeFloatBE.js",
+ "size": "browserify -r ./ | uglifyjs -c -m | gzip | wc -c",
+ "test": "standard && node ./bin/test.js",
+ "test-browser-es5": "airtap -- test/*.js",
+ "test-browser-es5-local": "airtap --local -- test/*.js",
+ "test-browser-es6": "airtap -- test/*.js test/node/*.js",
+ "test-browser-es6-local": "airtap --local -- test/*.js test/node/*.js",
+ "test-node": "tape test/*.js test/node/*.js",
+ "update-authors": "./bin/update-authors.sh"
+ },
+ "standard": {
+ "ignore": [
+ "test/node/**/*.js",
+ "test/common.js",
+ "test/_polyfill.js",
+ "perf/**/*.js"
+ ],
+ "globals": [
+ "SharedArrayBuffer"
+ ]
+ },
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/History.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/History.md
new file mode 100644
index 000000000..d60ce0e6d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/History.md
@@ -0,0 +1,97 @@
+3.1.2 / 2022-01-27
+==================
+
+ * Fix return value for un-parsable strings
+
+3.1.1 / 2021-11-15
+==================
+
+ * Fix "thousandsSeparator" incorrecting formatting fractional part
+
+3.1.0 / 2019-01-22
+==================
+
+ * Add petabyte (`pb`) support
+
+3.0.0 / 2017-08-31
+==================
+
+ * Change "kB" to "KB" in format output
+ * Remove support for Node.js 0.6
+ * Remove support for ComponentJS
+
+2.5.0 / 2017-03-24
+==================
+
+ * Add option "unit"
+
+2.4.0 / 2016-06-01
+==================
+
+ * Add option "unitSeparator"
+
+2.3.0 / 2016-02-15
+==================
+
+ * Drop partial bytes on all parsed units
+ * Fix non-finite numbers to `.format` to return `null`
+ * Fix parsing byte string that looks like hex
+ * perf: hoist regular expressions
+
+2.2.0 / 2015-11-13
+==================
+
+ * add option "decimalPlaces"
+ * add option "fixedDecimals"
+
+2.1.0 / 2015-05-21
+==================
+
+ * add `.format` export
+ * add `.parse` export
+
+2.0.2 / 2015-05-20
+==================
+
+ * remove map recreation
+ * remove unnecessary object construction
+
+2.0.1 / 2015-05-07
+==================
+
+ * fix browserify require
+ * remove node.extend dependency
+
+2.0.0 / 2015-04-12
+==================
+
+ * add option "case"
+ * add option "thousandsSeparator"
+ * return "null" on invalid parse input
+ * support proper round-trip: bytes(bytes(num)) === num
+ * units no longer case sensitive when parsing
+
+1.0.0 / 2014-05-05
+==================
+
+ * add negative support. fixes #6
+
+0.3.0 / 2014-03-19
+==================
+
+ * added terabyte support
+
+0.2.1 / 2013-04-01
+==================
+
+ * add .component
+
+0.2.0 / 2012-10-28
+==================
+
+ * bytes(200).should.eql('200b')
+
+0.1.0 / 2012-07-04
+==================
+
+ * add bytes to string conversion [yields]
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/LICENSE
new file mode 100644
index 000000000..63e95a963
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/LICENSE
@@ -0,0 +1,23 @@
+(The MIT License)
+
+Copyright (c) 2012-2014 TJ Holowaychuk
+Copyright (c) 2015 Jed Watson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/Readme.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/Readme.md
new file mode 100644
index 000000000..5790e23e3
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/Readme.md
@@ -0,0 +1,152 @@
+# Bytes utility
+
+[![NPM Version][npm-image]][npm-url]
+[![NPM Downloads][downloads-image]][downloads-url]
+[![Build Status][ci-image]][ci-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+
+Utility to parse a string bytes (ex: `1TB`) to bytes (`1099511627776`) and vice-versa.
+
+## Installation
+
+This is a [Node.js](https://nodejs.org/en/) module available through the
+[npm registry](https://www.npmjs.com/). Installation is done using the
+[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
+
+```bash
+$ npm install bytes
+```
+
+## Usage
+
+```js
+var bytes = require('bytes');
+```
+
+#### bytes(number|string value, [options]): number|string|null
+
+Default export function. Delegates to either `bytes.format` or `bytes.parse` based on the type of `value`.
+
+**Arguments**
+
+| Name | Type | Description |
+|---------|----------|--------------------|
+| value | `number`|`string` | Number value to format or string value to parse |
+| options | `Object` | Conversion options for `format` |
+
+**Returns**
+
+| Name | Type | Description |
+|---------|------------------|-------------------------------------------------|
+| results | `string`|`number`|`null` | Return null upon error. Numeric value in bytes, or string value otherwise. |
+
+**Example**
+
+```js
+bytes(1024);
+// output: '1KB'
+
+bytes('1KB');
+// output: 1024
+```
+
+#### bytes.format(number value, [options]): string|null
+
+Format the given value in bytes into a string. If the value is negative, it is kept as such. If it is a float, it is
+ rounded.
+
+**Arguments**
+
+| Name | Type | Description |
+|---------|----------|--------------------|
+| value | `number` | Value in bytes |
+| options | `Object` | Conversion options |
+
+**Options**
+
+| Property | Type | Description |
+|-------------------|--------|-----------------------------------------------------------------------------------------|
+| decimalPlaces | `number`|`null` | Maximum number of decimal places to include in output. Default value to `2`. |
+| fixedDecimals | `boolean`|`null` | Whether to always display the maximum number of decimal places. Default value to `false` |
+| thousandsSeparator | `string`|`null` | Example of values: `' '`, `','` and `'.'`... Default value to `''`. |
+| unit | `string`|`null` | The unit in which the result will be returned (B/KB/MB/GB/TB). Default value to `''` (which means auto detect). |
+| unitSeparator | `string`|`null` | Separator to use between number and unit. Default value to `''`. |
+
+**Returns**
+
+| Name | Type | Description |
+|---------|------------------|-------------------------------------------------|
+| results | `string`|`null` | Return null upon error. String value otherwise. |
+
+**Example**
+
+```js
+bytes.format(1024);
+// output: '1KB'
+
+bytes.format(1000);
+// output: '1000B'
+
+bytes.format(1000, {thousandsSeparator: ' '});
+// output: '1 000B'
+
+bytes.format(1024 * 1.7, {decimalPlaces: 0});
+// output: '2KB'
+
+bytes.format(1024, {unitSeparator: ' '});
+// output: '1 KB'
+```
+
+#### bytes.parse(string|number value): number|null
+
+Parse the string value into an integer in bytes. If no unit is given, or `value`
+is a number, it is assumed the value is in bytes.
+
+Supported units and abbreviations are as follows and are case-insensitive:
+
+ * `b` for bytes
+ * `kb` for kilobytes
+ * `mb` for megabytes
+ * `gb` for gigabytes
+ * `tb` for terabytes
+ * `pb` for petabytes
+
+The units are in powers of two, not ten. This means 1kb = 1024b according to this parser.
+
+**Arguments**
+
+| Name | Type | Description |
+|---------------|--------|--------------------|
+| value | `string`|`number` | String to parse, or number in bytes. |
+
+**Returns**
+
+| Name | Type | Description |
+|---------|-------------|-------------------------|
+| results | `number`|`null` | Return null upon error. Value in bytes otherwise. |
+
+**Example**
+
+```js
+bytes.parse('1KB');
+// output: 1024
+
+bytes.parse('1024');
+// output: 1024
+
+bytes.parse(1024);
+// output: 1024
+```
+
+## License
+
+[MIT](LICENSE)
+
+[ci-image]: https://badgen.net/github/checks/visionmedia/bytes.js/master?label=ci
+[ci-url]: https://github.com/visionmedia/bytes.js/actions?query=workflow%3Aci
+[coveralls-image]: https://badgen.net/coveralls/c/github/visionmedia/bytes.js/master
+[coveralls-url]: https://coveralls.io/r/visionmedia/bytes.js?branch=master
+[downloads-image]: https://badgen.net/npm/dm/bytes
+[downloads-url]: https://npmjs.org/package/bytes
+[npm-image]: https://badgen.net/npm/v/bytes
+[npm-url]: https://npmjs.org/package/bytes
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/index.js
new file mode 100644
index 000000000..6f2d0f89e
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/index.js
@@ -0,0 +1,170 @@
+/*!
+ * bytes
+ * Copyright(c) 2012-2014 TJ Holowaychuk
+ * Copyright(c) 2015 Jed Watson
+ * MIT Licensed
+ */
+
+'use strict';
+
+/**
+ * Module exports.
+ * @public
+ */
+
+module.exports = bytes;
+module.exports.format = format;
+module.exports.parse = parse;
+
+/**
+ * Module variables.
+ * @private
+ */
+
+var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g;
+
+var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/;
+
+var map = {
+ b: 1,
+ kb: 1 << 10,
+ mb: 1 << 20,
+ gb: 1 << 30,
+ tb: Math.pow(1024, 4),
+ pb: Math.pow(1024, 5),
+};
+
+var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i;
+
+/**
+ * Convert the given value in bytes into a string or parse to string to an integer in bytes.
+ *
+ * @param {string|number} value
+ * @param {{
+ * case: [string],
+ * decimalPlaces: [number]
+ * fixedDecimals: [boolean]
+ * thousandsSeparator: [string]
+ * unitSeparator: [string]
+ * }} [options] bytes options.
+ *
+ * @returns {string|number|null}
+ */
+
+function bytes(value, options) {
+ if (typeof value === 'string') {
+ return parse(value);
+ }
+
+ if (typeof value === 'number') {
+ return format(value, options);
+ }
+
+ return null;
+}
+
+/**
+ * Format the given value in bytes into a string.
+ *
+ * If the value is negative, it is kept as such. If it is a float,
+ * it is rounded.
+ *
+ * @param {number} value
+ * @param {object} [options]
+ * @param {number} [options.decimalPlaces=2]
+ * @param {number} [options.fixedDecimals=false]
+ * @param {string} [options.thousandsSeparator=]
+ * @param {string} [options.unit=]
+ * @param {string} [options.unitSeparator=]
+ *
+ * @returns {string|null}
+ * @public
+ */
+
+function format(value, options) {
+ if (!Number.isFinite(value)) {
+ return null;
+ }
+
+ var mag = Math.abs(value);
+ var thousandsSeparator = (options && options.thousandsSeparator) || '';
+ var unitSeparator = (options && options.unitSeparator) || '';
+ var decimalPlaces = (options && options.decimalPlaces !== undefined) ? options.decimalPlaces : 2;
+ var fixedDecimals = Boolean(options && options.fixedDecimals);
+ var unit = (options && options.unit) || '';
+
+ if (!unit || !map[unit.toLowerCase()]) {
+ if (mag >= map.pb) {
+ unit = 'PB';
+ } else if (mag >= map.tb) {
+ unit = 'TB';
+ } else if (mag >= map.gb) {
+ unit = 'GB';
+ } else if (mag >= map.mb) {
+ unit = 'MB';
+ } else if (mag >= map.kb) {
+ unit = 'KB';
+ } else {
+ unit = 'B';
+ }
+ }
+
+ var val = value / map[unit.toLowerCase()];
+ var str = val.toFixed(decimalPlaces);
+
+ if (!fixedDecimals) {
+ str = str.replace(formatDecimalsRegExp, '$1');
+ }
+
+ if (thousandsSeparator) {
+ str = str.split('.').map(function (s, i) {
+ return i === 0
+ ? s.replace(formatThousandsRegExp, thousandsSeparator)
+ : s
+ }).join('.');
+ }
+
+ return str + unitSeparator + unit;
+}
+
+/**
+ * Parse the string value into an integer in bytes.
+ *
+ * If no unit is given, it is assumed the value is in bytes.
+ *
+ * @param {number|string} val
+ *
+ * @returns {number|null}
+ * @public
+ */
+
+function parse(val) {
+ if (typeof val === 'number' && !isNaN(val)) {
+ return val;
+ }
+
+ if (typeof val !== 'string') {
+ return null;
+ }
+
+ // Test if the string passed is valid
+ var results = parseRegExp.exec(val);
+ var floatValue;
+ var unit = 'b';
+
+ if (!results) {
+ // Nothing could be extracted from the given string
+ floatValue = parseInt(val, 10);
+ unit = 'b'
+ } else {
+ // Retrieve the value and the unit
+ floatValue = parseFloat(results[1]);
+ unit = results[4].toLowerCase();
+ }
+
+ if (isNaN(floatValue)) {
+ return null;
+ }
+
+ return Math.floor(map[unit] * floatValue);
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/package.json
new file mode 100644
index 000000000..f2b6a8b0e
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/bytes/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "bytes",
+ "description": "Utility to parse a string bytes to bytes and vice-versa",
+ "version": "3.1.2",
+ "author": "TJ Holowaychuk (http://tjholowaychuk.com)",
+ "contributors": [
+ "Jed Watson ",
+ "Théo FIDRY "
+ ],
+ "license": "MIT",
+ "keywords": [
+ "byte",
+ "bytes",
+ "utility",
+ "parse",
+ "parser",
+ "convert",
+ "converter"
+ ],
+ "repository": "visionmedia/bytes.js",
+ "devDependencies": {
+ "eslint": "7.32.0",
+ "eslint-plugin-markdown": "2.2.1",
+ "mocha": "9.2.0",
+ "nyc": "15.1.0"
+ },
+ "files": [
+ "History.md",
+ "LICENSE",
+ "Readme.md",
+ "index.js"
+ ],
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "test": "mocha --check-leaks --reporter spec",
+ "test-ci": "nyc --reporter=lcov --reporter=text npm test",
+ "test-cov": "nyc --reporter=html --reporter=text npm test"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/LICENSE.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/LICENSE.md
new file mode 100644
index 000000000..8d28acf86
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/LICENSE.md
@@ -0,0 +1,16 @@
+ISC License
+
+Copyright (c) npm, Inc.
+
+Permission to use, copy, modify, and/or distribute this software for
+any purpose with or without fee is hereby granted, provided that the
+above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT HOLDER DISCLAIMS
+ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/README.md
new file mode 100644
index 000000000..6dc11babf
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/README.md
@@ -0,0 +1,703 @@
+# cacache [](https://npm.im/cacache) [](https://npm.im/cacache) [](https://travis-ci.org/npm/cacache) [](https://ci.appveyor.com/project/npm/cacache) [](https://coveralls.io/github/npm/cacache?branch=latest)
+
+[`cacache`](https://github.com/npm/cacache) is a Node.js library for managing
+local key and content address caches. It's really fast, really good at
+concurrency, and it will never give you corrupted data, even if cache files
+get corrupted or manipulated.
+
+On systems that support user and group settings on files, cacache will
+match the `uid` and `gid` values to the folder where the cache lives, even
+when running as `root`.
+
+It was written to be used as [npm](https://npm.im)'s local cache, but can
+just as easily be used on its own.
+
+## Install
+
+`$ npm install --save cacache`
+
+## Table of Contents
+
+* [Example](#example)
+* [Features](#features)
+* [Contributing](#contributing)
+* [API](#api)
+ * [Using localized APIs](#localized-api)
+ * Reading
+ * [`ls`](#ls)
+ * [`ls.stream`](#ls-stream)
+ * [`get`](#get-data)
+ * [`get.stream`](#get-stream)
+ * [`get.info`](#get-info)
+ * [`get.hasContent`](#get-hasContent)
+ * Writing
+ * [`put`](#put-data)
+ * [`put.stream`](#put-stream)
+ * [`rm.all`](#rm-all)
+ * [`rm.entry`](#rm-entry)
+ * [`rm.content`](#rm-content)
+ * [`index.compact`](#index-compact)
+ * [`index.insert`](#index-insert)
+ * Utilities
+ * [`clearMemoized`](#clear-memoized)
+ * [`tmp.mkdir`](#tmp-mkdir)
+ * [`tmp.withTmp`](#with-tmp)
+ * Integrity
+ * [Subresource Integrity](#integrity)
+ * [`verify`](#verify)
+ * [`verify.lastRun`](#verify-last-run)
+
+### Example
+
+```javascript
+const cacache = require('cacache')
+const fs = require('fs')
+
+const tarball = '/path/to/mytar.tgz'
+const cachePath = '/tmp/my-toy-cache'
+const key = 'my-unique-key-1234'
+
+// Cache it! Use `cachePath` as the root of the content cache
+cacache.put(cachePath, key, '10293801983029384').then(integrity => {
+ console.log(`Saved content to ${cachePath}.`)
+})
+
+const destination = '/tmp/mytar.tgz'
+
+// Copy the contents out of the cache and into their destination!
+// But this time, use stream instead!
+cacache.get.stream(
+ cachePath, key
+).pipe(
+ fs.createWriteStream(destination)
+).on('finish', () => {
+ console.log('done extracting!')
+})
+
+// The same thing, but skip the key index.
+cacache.get.byDigest(cachePath, integrityHash).then(data => {
+ fs.writeFile(destination, data, err => {
+ console.log('tarball data fetched based on its sha512sum and written out!')
+ })
+})
+```
+
+### Features
+
+* Extraction by key or by content address (shasum, etc)
+* [Subresource Integrity](#integrity) web standard support
+* Multi-hash support - safely host sha1, sha512, etc, in a single cache
+* Automatic content deduplication
+* Fault tolerance (immune to corruption, partial writes, process races, etc)
+* Consistency guarantees on read and write (full data verification)
+* Lockless, high-concurrency cache access
+* Streaming support
+* Promise support
+* Fast -- sub-millisecond reads and writes including verification
+* Arbitrary metadata storage
+* Garbage collection and additional offline verification
+* Thorough test coverage
+* There's probably a bloom filter in there somewhere. Those are cool, right? 🤔
+
+### Contributing
+
+The cacache team enthusiastically welcomes contributions and project participation! There's a bunch of things you can do if you want to contribute! The [Contributor Guide](CONTRIBUTING.md) has all the information you need for everything from reporting bugs to contributing entire new features. Please don't hesitate to jump in if you'd like to, or even ask us questions if something isn't clear.
+
+All participants and maintainers in this project are expected to follow [Code of Conduct](CODE_OF_CONDUCT.md), and just generally be excellent to each other.
+
+Please refer to the [Changelog](CHANGELOG.md) for project history details, too.
+
+Happy hacking!
+
+### API
+
+#### `> cacache.ls(cache) -> Promise`
+
+Lists info for all entries currently in the cache as a single large object. Each
+entry in the object will be keyed by the unique index key, with corresponding
+[`get.info`](#get-info) objects as the values.
+
+##### Example
+
+```javascript
+cacache.ls(cachePath).then(console.log)
+// Output
+{
+ 'my-thing': {
+ key: 'my-thing',
+ integrity: 'sha512-BaSe64/EnCoDED+HAsh=='
+ path: '.testcache/content/deadbeef', // joined with `cachePath`
+ time: 12345698490,
+ size: 4023948,
+ metadata: {
+ name: 'blah',
+ version: '1.2.3',
+ description: 'this was once a package but now it is my-thing'
+ }
+ },
+ 'other-thing': {
+ key: 'other-thing',
+ integrity: 'sha1-ANothER+hasH=',
+ path: '.testcache/content/bada55',
+ time: 11992309289,
+ size: 111112
+ }
+}
+```
+
+#### `> cacache.ls.stream(cache) -> Readable`
+
+Lists info for all entries currently in the cache as a single large object.
+
+This works just like [`ls`](#ls), except [`get.info`](#get-info) entries are
+returned as `'data'` events on the returned stream.
+
+##### Example
+
+```javascript
+cacache.ls.stream(cachePath).on('data', console.log)
+// Output
+{
+ key: 'my-thing',
+ integrity: 'sha512-BaSe64HaSh',
+ path: '.testcache/content/deadbeef', // joined with `cachePath`
+ time: 12345698490,
+ size: 13423,
+ metadata: {
+ name: 'blah',
+ version: '1.2.3',
+ description: 'this was once a package but now it is my-thing'
+ }
+}
+
+{
+ key: 'other-thing',
+ integrity: 'whirlpool-WoWSoMuchSupport',
+ path: '.testcache/content/bada55',
+ time: 11992309289,
+ size: 498023984029
+}
+
+{
+ ...
+}
+```
+
+#### `> cacache.get(cache, key, [opts]) -> Promise({data, metadata, integrity})`
+
+Returns an object with the cached data, digest, and metadata identified by
+`key`. The `data` property of this object will be a `Buffer` instance that
+presumably holds some data that means something to you. I'm sure you know what
+to do with it! cacache just won't care.
+
+`integrity` is a [Subresource
+Integrity](#integrity)
+string. That is, a string that can be used to verify `data`, which looks like
+`-`.
+
+If there is no content identified by `key`, or if the locally-stored data does
+not pass the validity checksum, the promise will be rejected.
+
+A sub-function, `get.byDigest` may be used for identical behavior, except lookup
+will happen by integrity hash, bypassing the index entirely. This version of the
+function *only* returns `data` itself, without any wrapper.
+
+See: [options](#get-options)
+
+##### Note
+
+This function loads the entire cache entry into memory before returning it. If
+you're dealing with Very Large data, consider using [`get.stream`](#get-stream)
+instead.
+
+##### Example
+
+```javascript
+// Look up by key
+cache.get(cachePath, 'my-thing').then(console.log)
+// Output:
+{
+ metadata: {
+ thingName: 'my'
+ },
+ integrity: 'sha512-BaSe64HaSh',
+ data: Buffer#,
+ size: 9320
+}
+
+// Look up by digest
+cache.get.byDigest(cachePath, 'sha512-BaSe64HaSh').then(console.log)
+// Output:
+Buffer#
+```
+
+#### `> cacache.get.stream(cache, key, [opts]) -> Readable`
+
+Returns a [Readable Stream](https://nodejs.org/api/stream.html#stream_readable_streams) of the cached data identified by `key`.
+
+If there is no content identified by `key`, or if the locally-stored data does
+not pass the validity checksum, an error will be emitted.
+
+`metadata` and `integrity` events will be emitted before the stream closes, if
+you need to collect that extra data about the cached entry.
+
+A sub-function, `get.stream.byDigest` may be used for identical behavior,
+except lookup will happen by integrity hash, bypassing the index entirely. This
+version does not emit the `metadata` and `integrity` events at all.
+
+See: [options](#get-options)
+
+##### Example
+
+```javascript
+// Look up by key
+cache.get.stream(
+ cachePath, 'my-thing'
+).on('metadata', metadata => {
+ console.log('metadata:', metadata)
+}).on('integrity', integrity => {
+ console.log('integrity:', integrity)
+}).pipe(
+ fs.createWriteStream('./x.tgz')
+)
+// Outputs:
+metadata: { ... }
+integrity: 'sha512-SoMeDIGest+64=='
+
+// Look up by digest
+cache.get.stream.byDigest(
+ cachePath, 'sha512-SoMeDIGest+64=='
+).pipe(
+ fs.createWriteStream('./x.tgz')
+)
+```
+
+#### `> cacache.get.info(cache, key) -> Promise`
+
+Looks up `key` in the cache index, returning information about the entry if
+one exists.
+
+##### Fields
+
+* `key` - Key the entry was looked up under. Matches the `key` argument.
+* `integrity` - [Subresource Integrity hash](#integrity) for the content this entry refers to.
+* `path` - Filesystem path where content is stored, joined with `cache` argument.
+* `time` - Timestamp the entry was first added on.
+* `metadata` - User-assigned metadata associated with the entry/content.
+
+##### Example
+
+```javascript
+cacache.get.info(cachePath, 'my-thing').then(console.log)
+
+// Output
+{
+ key: 'my-thing',
+ integrity: 'sha256-MUSTVERIFY+ALL/THINGS=='
+ path: '.testcache/content/deadbeef',
+ time: 12345698490,
+ size: 849234,
+ metadata: {
+ name: 'blah',
+ version: '1.2.3',
+ description: 'this was once a package but now it is my-thing'
+ }
+}
+```
+
+#### `> cacache.get.hasContent(cache, integrity) -> Promise`
+
+Looks up a [Subresource Integrity hash](#integrity) in the cache. If content
+exists for this `integrity`, it will return an object, with the specific single integrity hash
+that was found in `sri` key, and the size of the found content as `size`. If no content exists for this integrity, it will return `false`.
+
+##### Example
+
+```javascript
+cacache.get.hasContent(cachePath, 'sha256-MUSTVERIFY+ALL/THINGS==').then(console.log)
+
+// Output
+{
+ sri: {
+ source: 'sha256-MUSTVERIFY+ALL/THINGS==',
+ algorithm: 'sha256',
+ digest: 'MUSTVERIFY+ALL/THINGS==',
+ options: []
+ },
+ size: 9001
+}
+
+cacache.get.hasContent(cachePath, 'sha521-NOT+IN/CACHE==').then(console.log)
+
+// Output
+false
+```
+
+##### Options
+
+##### `opts.integrity`
+If present, the pre-calculated digest for the inserted content. If this option
+is provided and does not match the post-insertion digest, insertion will fail
+with an `EINTEGRITY` error.
+
+##### `opts.memoize`
+
+Default: null
+
+If explicitly truthy, cacache will read from memory and memoize data on bulk read. If `false`, cacache will read from disk data. Reader functions by default read from in-memory cache.
+
+##### `opts.size`
+If provided, the data stream will be verified to check that enough data was
+passed through. If there's more or less data than expected, insertion will fail
+with an `EBADSIZE` error.
+
+
+#### `> cacache.put(cache, key, data, [opts]) -> Promise`
+
+Inserts data passed to it into the cache. The returned Promise resolves with a
+digest (generated according to [`opts.algorithms`](#optsalgorithms)) after the
+cache entry has been successfully written.
+
+See: [options](#put-options)
+
+##### Example
+
+```javascript
+fetch(
+ 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz'
+).then(data => {
+ return cacache.put(cachePath, 'registry.npmjs.org|cacache@1.0.0', data)
+}).then(integrity => {
+ console.log('integrity hash is', integrity)
+})
+```
+
+#### `> cacache.put.stream(cache, key, [opts]) -> Writable`
+
+Returns a [Writable
+Stream](https://nodejs.org/api/stream.html#stream_writable_streams) that inserts
+data written to it into the cache. Emits an `integrity` event with the digest of
+written contents when it succeeds.
+
+See: [options](#put-options)
+
+##### Example
+
+```javascript
+request.get(
+ 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz'
+).pipe(
+ cacache.put.stream(
+ cachePath, 'registry.npmjs.org|cacache@1.0.0'
+ ).on('integrity', d => console.log(`integrity digest is ${d}`))
+)
+```
+
+##### Options
+
+##### `opts.metadata`
+
+Arbitrary metadata to be attached to the inserted key.
+
+##### `opts.size`
+
+If provided, the data stream will be verified to check that enough data was
+passed through. If there's more or less data than expected, insertion will fail
+with an `EBADSIZE` error.
+
+##### `opts.integrity`
+
+If present, the pre-calculated digest for the inserted content. If this option
+is provided and does not match the post-insertion digest, insertion will fail
+with an `EINTEGRITY` error.
+
+`algorithms` has no effect if this option is present.
+
+##### `opts.algorithms`
+
+Default: ['sha512']
+
+Hashing algorithms to use when calculating the [subresource integrity
+digest](#integrity)
+for inserted data. Can use any algorithm listed in `crypto.getHashes()` or
+`'omakase'`/`'お任せします'` to pick a random hash algorithm on each insertion. You
+may also use any anagram of `'modnar'` to use this feature.
+
+Currently only supports one algorithm at a time (i.e., an array length of
+exactly `1`). Has no effect if `opts.integrity` is present.
+
+##### `opts.memoize`
+
+Default: null
+
+If provided, cacache will memoize the given cache insertion in memory, bypassing
+any filesystem checks for that key or digest in future cache fetches. Nothing
+will be written to the in-memory cache unless this option is explicitly truthy.
+
+If `opts.memoize` is an object or a `Map`-like (that is, an object with `get`
+and `set` methods), it will be written to instead of the global memoization
+cache.
+
+Reading from disk data can be forced by explicitly passing `memoize: false` to
+the reader functions, but their default will be to read from memory.
+
+##### `opts.tmpPrefix`
+Default: null
+
+Prefix to append on the temporary directory name inside the cache's tmp dir.
+
+#### `> cacache.rm.all(cache) -> Promise`
+
+Clears the entire cache. Mainly by blowing away the cache directory itself.
+
+##### Example
+
+```javascript
+cacache.rm.all(cachePath).then(() => {
+ console.log('THE APOCALYPSE IS UPON US 😱')
+})
+```
+
+#### `> cacache.rm.entry(cache, key, [opts]) -> Promise`
+
+Alias: `cacache.rm`
+
+Removes the index entry for `key`. Content will still be accessible if
+requested directly by content address ([`get.stream.byDigest`](#get-stream)).
+
+By default, this appends a new entry to the index with an integrity of `null`.
+If `opts.removeFully` is set to `true` then the index file itself will be
+physically deleted rather than appending a `null`.
+
+To remove the content itself (which might still be used by other entries), use
+[`rm.content`](#rm-content). Or, to safely vacuum any unused content, use
+[`verify`](#verify).
+
+##### Example
+
+```javascript
+cacache.rm.entry(cachePath, 'my-thing').then(() => {
+ console.log('I did not like it anyway')
+})
+```
+
+#### `> cacache.rm.content(cache, integrity) -> Promise`
+
+Removes the content identified by `integrity`. Any index entries referring to it
+will not be usable again until the content is re-added to the cache with an
+identical digest.
+
+##### Example
+
+```javascript
+cacache.rm.content(cachePath, 'sha512-SoMeDIGest/IN+BaSE64==').then(() => {
+ console.log('data for my-thing is gone!')
+})
+```
+
+#### `> cacache.index.compact(cache, key, matchFn, [opts]) -> Promise`
+
+Uses `matchFn`, which must be a synchronous function that accepts two entries
+and returns a boolean indicating whether or not the two entries match, to
+deduplicate all entries in the cache for the given `key`.
+
+If `opts.validateEntry` is provided, it will be called as a function with the
+only parameter being a single index entry. The function must return a Boolean,
+if it returns `true` the entry is considered valid and will be kept in the index,
+if it returns `false` the entry will be removed from the index.
+
+If `opts.validateEntry` is not provided, however, every entry in the index will
+be deduplicated and kept until the first `null` integrity is reached, removing
+all entries that were written before the `null`.
+
+The deduplicated list of entries is both written to the index, replacing the
+existing content, and returned in the Promise.
+
+#### `> cacache.index.insert(cache, key, integrity, opts) -> Promise`
+
+Writes an index entry to the cache for the given `key` without writing content.
+
+It is assumed if you are using this method, you have already stored the content
+some other way and you only wish to add a new index to that content. The `metadata`
+and `size` properties are read from `opts` and used as part of the index entry.
+
+Returns a Promise resolving to the newly added entry.
+
+#### `> cacache.clearMemoized()`
+
+Completely resets the in-memory entry cache.
+
+#### `> tmp.mkdir(cache, opts) -> Promise`
+
+Returns a unique temporary directory inside the cache's `tmp` dir. This
+directory will use the same safe user assignment that all the other stuff use.
+
+Once the directory is made, it's the user's responsibility that all files
+within are given the appropriate `gid`/`uid` ownership settings to match
+the rest of the cache. If not, you can ask cacache to do it for you by
+calling [`tmp.fix()`](#tmp-fix), which will fix all tmp directory
+permissions.
+
+If you want automatic cleanup of this directory, use
+[`tmp.withTmp()`](#with-tpm)
+
+See: [options](#tmp-options)
+
+##### Example
+
+```javascript
+cacache.tmp.mkdir(cache).then(dir => {
+ fs.writeFile(path.join(dir, 'blablabla'), Buffer#<1234>, ...)
+})
+```
+
+#### `> tmp.fix(cache) -> Promise`
+
+Sets the `uid` and `gid` properties on all files and folders within the tmp
+folder to match the rest of the cache.
+
+Use this after manually writing files into [`tmp.mkdir`](#tmp-mkdir) or
+[`tmp.withTmp`](#with-tmp).
+
+##### Example
+
+```javascript
+cacache.tmp.mkdir(cache).then(dir => {
+ writeFile(path.join(dir, 'file'), someData).then(() => {
+ // make sure we didn't just put a root-owned file in the cache
+ cacache.tmp.fix().then(() => {
+ // all uids and gids match now
+ })
+ })
+})
+```
+
+#### `> tmp.withTmp(cache, opts, cb) -> Promise`
+
+Creates a temporary directory with [`tmp.mkdir()`](#tmp-mkdir) and calls `cb`
+with it. The created temporary directory will be removed when the return value
+of `cb()` resolves, the tmp directory will be automatically deleted once that
+promise completes.
+
+The same caveats apply when it comes to managing permissions for the tmp dir's
+contents.
+
+See: [options](#tmp-options)
+
+##### Example
+
+```javascript
+cacache.tmp.withTmp(cache, dir => {
+ return fs.writeFileAsync(path.join(dir, 'blablabla'), Buffer#<1234>, ...)
+}).then(() => {
+ // `dir` no longer exists
+})
+```
+
+##### Options
+
+##### `opts.tmpPrefix`
+Default: null
+
+Prefix to append on the temporary directory name inside the cache's tmp dir.
+
+#### Subresource Integrity Digests
+
+For content verification and addressing, cacache uses strings following the
+[Subresource
+Integrity spec](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity).
+That is, any time cacache expects an `integrity` argument or option, it
+should be in the format `-`.
+
+One deviation from the current spec is that cacache will support any hash
+algorithms supported by the underlying Node.js process. You can use
+`crypto.getHashes()` to see which ones you can use.
+
+##### Generating Digests Yourself
+
+If you have an existing content shasum, they are generally formatted as a
+hexadecimal string (that is, a sha1 would look like:
+`5f5513f8822fdbe5145af33b64d8d970dcf95c6e`). In order to be compatible with
+cacache, you'll need to convert this to an equivalent subresource integrity
+string. For this example, the corresponding hash would be:
+`sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4=`.
+
+If you want to generate an integrity string yourself for existing data, you can
+use something like this:
+
+```javascript
+const crypto = require('crypto')
+const hashAlgorithm = 'sha512'
+const data = 'foobarbaz'
+
+const integrity = (
+ hashAlgorithm +
+ '-' +
+ crypto.createHash(hashAlgorithm).update(data).digest('base64')
+)
+```
+
+You can also use [`ssri`](https://npm.im/ssri) to have a richer set of functionality
+around SRI strings, including generation, parsing, and translating from existing
+hex-formatted strings.
+
+#### `> cacache.verify(cache, opts) -> Promise`
+
+Checks out and fixes up your cache:
+
+* Cleans up corrupted or invalid index entries.
+* Custom entry filtering options.
+* Garbage collects any content entries not referenced by the index.
+* Checks integrity for all content entries and removes invalid content.
+* Fixes cache ownership.
+* Removes the `tmp` directory in the cache and all its contents.
+
+When it's done, it'll return an object with various stats about the verification
+process, including amount of storage reclaimed, number of valid entries, number
+of entries removed, etc.
+
+##### Options
+
+##### `opts.concurrency`
+
+Default: 20
+
+Number of concurrently read files in the filesystem while doing clean up.
+
+##### `opts.filter`
+Receives a formatted entry. Return false to remove it.
+Note: might be called more than once on the same entry.
+
+##### `opts.log`
+Custom logger function:
+```
+ log: { silly () {} }
+ log.silly('verify', 'verifying cache at', cache)
+```
+
+##### Example
+
+```sh
+echo somegarbage >> $CACHEPATH/content/deadbeef
+```
+
+```javascript
+cacache.verify(cachePath).then(stats => {
+ // deadbeef collected, because of invalid checksum.
+ console.log('cache is much nicer now! stats:', stats)
+})
+```
+
+#### `> cacache.verify.lastRun(cache) -> Promise`
+
+Returns a `Date` representing the last time `cacache.verify` was run on `cache`.
+
+##### Example
+
+```javascript
+cacache.verify(cachePath).then(() => {
+ cacache.verify.lastRun(cachePath).then(lastTime => {
+ console.log('cacache.verify was last called on' + lastTime)
+ })
+})
+```
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/get.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/get.js
new file mode 100644
index 000000000..4e905e7cf
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/get.js
@@ -0,0 +1,237 @@
+'use strict'
+
+const Collect = require('minipass-collect')
+const Minipass = require('minipass')
+const Pipeline = require('minipass-pipeline')
+const fs = require('fs')
+const util = require('util')
+
+const index = require('./lib/entry-index')
+const memo = require('./lib/memoization')
+const read = require('./lib/content/read')
+
+const writeFile = util.promisify(fs.writeFile)
+
+function getData (cache, key, opts = {}) {
+ const { integrity, memoize, size } = opts
+ const memoized = memo.get(cache, key, opts)
+ if (memoized && memoize !== false) {
+ return Promise.resolve({
+ metadata: memoized.entry.metadata,
+ data: memoized.data,
+ integrity: memoized.entry.integrity,
+ size: memoized.entry.size,
+ })
+ }
+
+ return index.find(cache, key, opts).then((entry) => {
+ if (!entry)
+ throw new index.NotFoundError(cache, key)
+
+ return read(cache, entry.integrity, { integrity, size }).then((data) => {
+ if (memoize)
+ memo.put(cache, entry, data, opts)
+
+ return {
+ data,
+ metadata: entry.metadata,
+ size: entry.size,
+ integrity: entry.integrity,
+ }
+ })
+ })
+}
+module.exports = getData
+
+function getDataByDigest (cache, key, opts = {}) {
+ const { integrity, memoize, size } = opts
+ const memoized = memo.get.byDigest(cache, key, opts)
+ if (memoized && memoize !== false)
+ return Promise.resolve(memoized)
+
+ return read(cache, key, { integrity, size }).then((res) => {
+ if (memoize)
+ memo.put.byDigest(cache, key, res, opts)
+ return res
+ })
+}
+module.exports.byDigest = getDataByDigest
+
+function getDataSync (cache, key, opts = {}) {
+ const { integrity, memoize, size } = opts
+ const memoized = memo.get(cache, key, opts)
+
+ if (memoized && memoize !== false) {
+ return {
+ metadata: memoized.entry.metadata,
+ data: memoized.data,
+ integrity: memoized.entry.integrity,
+ size: memoized.entry.size,
+ }
+ }
+ const entry = index.find.sync(cache, key, opts)
+ if (!entry)
+ throw new index.NotFoundError(cache, key)
+ const data = read.sync(cache, entry.integrity, {
+ integrity: integrity,
+ size: size,
+ })
+ const res = {
+ metadata: entry.metadata,
+ data: data,
+ size: entry.size,
+ integrity: entry.integrity,
+ }
+ if (memoize)
+ memo.put(cache, entry, res.data, opts)
+
+ return res
+}
+
+module.exports.sync = getDataSync
+
+function getDataByDigestSync (cache, digest, opts = {}) {
+ const { integrity, memoize, size } = opts
+ const memoized = memo.get.byDigest(cache, digest, opts)
+
+ if (memoized && memoize !== false)
+ return memoized
+
+ const res = read.sync(cache, digest, {
+ integrity: integrity,
+ size: size,
+ })
+ if (memoize)
+ memo.put.byDigest(cache, digest, res, opts)
+
+ return res
+}
+module.exports.sync.byDigest = getDataByDigestSync
+
+const getMemoizedStream = (memoized) => {
+ const stream = new Minipass()
+ stream.on('newListener', function (ev, cb) {
+ ev === 'metadata' && cb(memoized.entry.metadata)
+ ev === 'integrity' && cb(memoized.entry.integrity)
+ ev === 'size' && cb(memoized.entry.size)
+ })
+ stream.end(memoized.data)
+ return stream
+}
+
+function getStream (cache, key, opts = {}) {
+ const { memoize, size } = opts
+ const memoized = memo.get(cache, key, opts)
+ if (memoized && memoize !== false)
+ return getMemoizedStream(memoized)
+
+ const stream = new Pipeline()
+ index
+ .find(cache, key)
+ .then((entry) => {
+ if (!entry)
+ throw new index.NotFoundError(cache, key)
+
+ stream.emit('metadata', entry.metadata)
+ stream.emit('integrity', entry.integrity)
+ stream.emit('size', entry.size)
+ stream.on('newListener', function (ev, cb) {
+ ev === 'metadata' && cb(entry.metadata)
+ ev === 'integrity' && cb(entry.integrity)
+ ev === 'size' && cb(entry.size)
+ })
+
+ const src = read.readStream(
+ cache,
+ entry.integrity,
+ { ...opts, size: typeof size !== 'number' ? entry.size : size }
+ )
+
+ if (memoize) {
+ const memoStream = new Collect.PassThrough()
+ memoStream.on('collect', data => memo.put(cache, entry, data, opts))
+ stream.unshift(memoStream)
+ }
+ stream.unshift(src)
+ })
+ .catch((err) => stream.emit('error', err))
+
+ return stream
+}
+
+module.exports.stream = getStream
+
+function getStreamDigest (cache, integrity, opts = {}) {
+ const { memoize } = opts
+ const memoized = memo.get.byDigest(cache, integrity, opts)
+ if (memoized && memoize !== false) {
+ const stream = new Minipass()
+ stream.end(memoized)
+ return stream
+ } else {
+ const stream = read.readStream(cache, integrity, opts)
+ if (!memoize)
+ return stream
+
+ const memoStream = new Collect.PassThrough()
+ memoStream.on('collect', data => memo.put.byDigest(
+ cache,
+ integrity,
+ data,
+ opts
+ ))
+ return new Pipeline(stream, memoStream)
+ }
+}
+
+module.exports.stream.byDigest = getStreamDigest
+
+function info (cache, key, opts = {}) {
+ const { memoize } = opts
+ const memoized = memo.get(cache, key, opts)
+ if (memoized && memoize !== false)
+ return Promise.resolve(memoized.entry)
+ else
+ return index.find(cache, key)
+}
+module.exports.info = info
+
+function copy (cache, key, dest, opts = {}) {
+ if (read.copy) {
+ return index.find(cache, key, opts).then((entry) => {
+ if (!entry)
+ throw new index.NotFoundError(cache, key)
+ return read.copy(cache, entry.integrity, dest, opts)
+ .then(() => {
+ return {
+ metadata: entry.metadata,
+ size: entry.size,
+ integrity: entry.integrity,
+ }
+ })
+ })
+ }
+
+ return getData(cache, key, opts).then((res) => {
+ return writeFile(dest, res.data).then(() => {
+ return {
+ metadata: res.metadata,
+ size: res.size,
+ integrity: res.integrity,
+ }
+ })
+ })
+}
+module.exports.copy = copy
+
+function copyByDigest (cache, key, dest, opts = {}) {
+ if (read.copy)
+ return read.copy(cache, key, dest, opts).then(() => key)
+
+ return getDataByDigest(cache, key, opts).then((res) => {
+ return writeFile(dest, res).then(() => key)
+ })
+}
+module.exports.copy.byDigest = copyByDigest
+
+module.exports.hasContent = read.hasContent
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/index.js
new file mode 100644
index 000000000..c8c52b041
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/index.js
@@ -0,0 +1,46 @@
+'use strict'
+
+const ls = require('./ls.js')
+const get = require('./get.js')
+const put = require('./put.js')
+const rm = require('./rm.js')
+const verify = require('./verify.js')
+const { clearMemoized } = require('./lib/memoization.js')
+const tmp = require('./lib/util/tmp.js')
+const index = require('./lib/entry-index.js')
+
+module.exports.index = {}
+module.exports.index.compact = index.compact
+module.exports.index.insert = index.insert
+
+module.exports.ls = ls
+module.exports.ls.stream = ls.stream
+
+module.exports.get = get
+module.exports.get.byDigest = get.byDigest
+module.exports.get.sync = get.sync
+module.exports.get.sync.byDigest = get.sync.byDigest
+module.exports.get.stream = get.stream
+module.exports.get.stream.byDigest = get.stream.byDigest
+module.exports.get.copy = get.copy
+module.exports.get.copy.byDigest = get.copy.byDigest
+module.exports.get.info = get.info
+module.exports.get.hasContent = get.hasContent
+module.exports.get.hasContent.sync = get.hasContent.sync
+
+module.exports.put = put
+module.exports.put.stream = put.stream
+
+module.exports.rm = rm.entry
+module.exports.rm.all = rm.all
+module.exports.rm.entry = module.exports.rm
+module.exports.rm.content = rm.content
+
+module.exports.clearMemoized = clearMemoized
+
+module.exports.tmp = {}
+module.exports.tmp.mkdir = tmp.mkdir
+module.exports.tmp.withTmp = tmp.withTmp
+
+module.exports.verify = verify
+module.exports.verify.lastRun = verify.lastRun
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/path.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/path.js
new file mode 100644
index 000000000..ad5a76a4f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/path.js
@@ -0,0 +1,29 @@
+'use strict'
+
+const contentVer = require('../../package.json')['cache-version'].content
+const hashToSegments = require('../util/hash-to-segments')
+const path = require('path')
+const ssri = require('ssri')
+
+// Current format of content file path:
+//
+// sha512-BaSE64Hex= ->
+// ~/.my-cache/content-v2/sha512/ba/da/55deadbeefc0ffee
+//
+module.exports = contentPath
+
+function contentPath (cache, integrity) {
+ const sri = ssri.parse(integrity, { single: true })
+ // contentPath is the *strongest* algo given
+ return path.join(
+ contentDir(cache),
+ sri.algorithm,
+ ...hashToSegments(sri.hexDigest())
+ )
+}
+
+module.exports.contentDir = contentDir
+
+function contentDir (cache) {
+ return path.join(cache, `content-v${contentVer}`)
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/read.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/read.js
new file mode 100644
index 000000000..034e8eee0
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/read.js
@@ -0,0 +1,244 @@
+'use strict'
+
+const util = require('util')
+
+const fs = require('fs')
+const fsm = require('fs-minipass')
+const ssri = require('ssri')
+const contentPath = require('./path')
+const Pipeline = require('minipass-pipeline')
+
+const lstat = util.promisify(fs.lstat)
+const readFile = util.promisify(fs.readFile)
+
+module.exports = read
+
+const MAX_SINGLE_READ_SIZE = 64 * 1024 * 1024
+function read (cache, integrity, opts = {}) {
+ const { size } = opts
+ return withContentSri(cache, integrity, (cpath, sri) => {
+ // get size
+ return lstat(cpath).then(stat => ({ stat, cpath, sri }))
+ }).then(({ stat, cpath, sri }) => {
+ if (typeof size === 'number' && stat.size !== size)
+ throw sizeError(size, stat.size)
+
+ if (stat.size > MAX_SINGLE_READ_SIZE)
+ return readPipeline(cpath, stat.size, sri, new Pipeline()).concat()
+
+ return readFile(cpath, null).then((data) => {
+ if (!ssri.checkData(data, sri))
+ throw integrityError(sri, cpath)
+
+ return data
+ })
+ })
+}
+
+const readPipeline = (cpath, size, sri, stream) => {
+ stream.push(
+ new fsm.ReadStream(cpath, {
+ size,
+ readSize: MAX_SINGLE_READ_SIZE,
+ }),
+ ssri.integrityStream({
+ integrity: sri,
+ size,
+ })
+ )
+ return stream
+}
+
+module.exports.sync = readSync
+
+function readSync (cache, integrity, opts = {}) {
+ const { size } = opts
+ return withContentSriSync(cache, integrity, (cpath, sri) => {
+ const data = fs.readFileSync(cpath)
+ if (typeof size === 'number' && size !== data.length)
+ throw sizeError(size, data.length)
+
+ if (ssri.checkData(data, sri))
+ return data
+
+ throw integrityError(sri, cpath)
+ })
+}
+
+module.exports.stream = readStream
+module.exports.readStream = readStream
+
+function readStream (cache, integrity, opts = {}) {
+ const { size } = opts
+ const stream = new Pipeline()
+ withContentSri(cache, integrity, (cpath, sri) => {
+ // just lstat to ensure it exists
+ return lstat(cpath).then((stat) => ({ stat, cpath, sri }))
+ }).then(({ stat, cpath, sri }) => {
+ if (typeof size === 'number' && size !== stat.size)
+ return stream.emit('error', sizeError(size, stat.size))
+
+ readPipeline(cpath, stat.size, sri, stream)
+ }, er => stream.emit('error', er))
+
+ return stream
+}
+
+let copyFile
+if (fs.copyFile) {
+ module.exports.copy = copy
+ module.exports.copy.sync = copySync
+ copyFile = util.promisify(fs.copyFile)
+}
+
+function copy (cache, integrity, dest) {
+ return withContentSri(cache, integrity, (cpath, sri) => {
+ return copyFile(cpath, dest)
+ })
+}
+
+function copySync (cache, integrity, dest) {
+ return withContentSriSync(cache, integrity, (cpath, sri) => {
+ return fs.copyFileSync(cpath, dest)
+ })
+}
+
+module.exports.hasContent = hasContent
+
+function hasContent (cache, integrity) {
+ if (!integrity)
+ return Promise.resolve(false)
+
+ return withContentSri(cache, integrity, (cpath, sri) => {
+ return lstat(cpath).then((stat) => ({ size: stat.size, sri, stat }))
+ }).catch((err) => {
+ if (err.code === 'ENOENT')
+ return false
+
+ if (err.code === 'EPERM') {
+ /* istanbul ignore else */
+ if (process.platform !== 'win32')
+ throw err
+ else
+ return false
+ }
+ })
+}
+
+module.exports.hasContent.sync = hasContentSync
+
+function hasContentSync (cache, integrity) {
+ if (!integrity)
+ return false
+
+ return withContentSriSync(cache, integrity, (cpath, sri) => {
+ try {
+ const stat = fs.lstatSync(cpath)
+ return { size: stat.size, sri, stat }
+ } catch (err) {
+ if (err.code === 'ENOENT')
+ return false
+
+ if (err.code === 'EPERM') {
+ /* istanbul ignore else */
+ if (process.platform !== 'win32')
+ throw err
+ else
+ return false
+ }
+ }
+ })
+}
+
+function withContentSri (cache, integrity, fn) {
+ const tryFn = () => {
+ const sri = ssri.parse(integrity)
+ // If `integrity` has multiple entries, pick the first digest
+ // with available local data.
+ const algo = sri.pickAlgorithm()
+ const digests = sri[algo]
+
+ if (digests.length <= 1) {
+ const cpath = contentPath(cache, digests[0])
+ return fn(cpath, digests[0])
+ } else {
+ // Can't use race here because a generic error can happen before
+ // a ENOENT error, and can happen before a valid result
+ return Promise
+ .all(digests.map((meta) => {
+ return withContentSri(cache, meta, fn)
+ .catch((err) => {
+ if (err.code === 'ENOENT') {
+ return Object.assign(
+ new Error('No matching content found for ' + sri.toString()),
+ { code: 'ENOENT' }
+ )
+ }
+ return err
+ })
+ }))
+ .then((results) => {
+ // Return the first non error if it is found
+ const result = results.find((r) => !(r instanceof Error))
+ if (result)
+ return result
+
+ // Throw the No matching content found error
+ const enoentError = results.find((r) => r.code === 'ENOENT')
+ if (enoentError)
+ throw enoentError
+
+ // Throw generic error
+ throw results.find((r) => r instanceof Error)
+ })
+ }
+ }
+
+ return new Promise((resolve, reject) => {
+ try {
+ tryFn()
+ .then(resolve)
+ .catch(reject)
+ } catch (err) {
+ reject(err)
+ }
+ })
+}
+
+function withContentSriSync (cache, integrity, fn) {
+ const sri = ssri.parse(integrity)
+ // If `integrity` has multiple entries, pick the first digest
+ // with available local data.
+ const algo = sri.pickAlgorithm()
+ const digests = sri[algo]
+ if (digests.length <= 1) {
+ const cpath = contentPath(cache, digests[0])
+ return fn(cpath, digests[0])
+ } else {
+ let lastErr = null
+ for (const meta of digests) {
+ try {
+ return withContentSriSync(cache, meta, fn)
+ } catch (err) {
+ lastErr = err
+ }
+ }
+ throw lastErr
+ }
+}
+
+function sizeError (expected, found) {
+ const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
+ err.expected = expected
+ err.found = found
+ err.code = 'EBADSIZE'
+ return err
+}
+
+function integrityError (sri, path) {
+ const err = new Error(`Integrity verification failed for ${sri} (${path})`)
+ err.code = 'EINTEGRITY'
+ err.sri = sri
+ err.path = path
+ return err
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/rm.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/rm.js
new file mode 100644
index 000000000..6a3d1a3d0
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/rm.js
@@ -0,0 +1,19 @@
+'use strict'
+
+const util = require('util')
+
+const contentPath = require('./path')
+const { hasContent } = require('./read')
+const rimraf = util.promisify(require('rimraf'))
+
+module.exports = rm
+
+function rm (cache, integrity) {
+ return hasContent(cache, integrity).then((content) => {
+ // ~pretty~ sure we can't end up with a content lacking sri, but be safe
+ if (content && content.sri)
+ return rimraf(contentPath(cache, content.sri)).then(() => true)
+ else
+ return false
+ })
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/write.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/write.js
new file mode 100644
index 000000000..dde1bd1dd
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/content/write.js
@@ -0,0 +1,189 @@
+'use strict'
+
+const util = require('util')
+
+const contentPath = require('./path')
+const fixOwner = require('../util/fix-owner')
+const fs = require('fs')
+const moveFile = require('../util/move-file')
+const Minipass = require('minipass')
+const Pipeline = require('minipass-pipeline')
+const Flush = require('minipass-flush')
+const path = require('path')
+const rimraf = util.promisify(require('rimraf'))
+const ssri = require('ssri')
+const uniqueFilename = require('unique-filename')
+const { disposer } = require('./../util/disposer')
+const fsm = require('fs-minipass')
+
+const writeFile = util.promisify(fs.writeFile)
+
+module.exports = write
+
+function write (cache, data, opts = {}) {
+ const { algorithms, size, integrity } = opts
+ if (algorithms && algorithms.length > 1)
+ throw new Error('opts.algorithms only supports a single algorithm for now')
+
+ if (typeof size === 'number' && data.length !== size)
+ return Promise.reject(sizeError(size, data.length))
+
+ const sri = ssri.fromData(data, algorithms ? { algorithms } : {})
+ if (integrity && !ssri.checkData(data, integrity, opts))
+ return Promise.reject(checksumError(integrity, sri))
+
+ return disposer(makeTmp(cache, opts), makeTmpDisposer,
+ (tmp) => {
+ return writeFile(tmp.target, data, { flag: 'wx' })
+ .then(() => moveToDestination(tmp, cache, sri, opts))
+ })
+ .then(() => ({ integrity: sri, size: data.length }))
+}
+
+module.exports.stream = writeStream
+
+// writes proxied to the 'inputStream' that is passed to the Promise
+// 'end' is deferred until content is handled.
+class CacacheWriteStream extends Flush {
+ constructor (cache, opts) {
+ super()
+ this.opts = opts
+ this.cache = cache
+ this.inputStream = new Minipass()
+ this.inputStream.on('error', er => this.emit('error', er))
+ this.inputStream.on('drain', () => this.emit('drain'))
+ this.handleContentP = null
+ }
+
+ write (chunk, encoding, cb) {
+ if (!this.handleContentP) {
+ this.handleContentP = handleContent(
+ this.inputStream,
+ this.cache,
+ this.opts
+ )
+ }
+ return this.inputStream.write(chunk, encoding, cb)
+ }
+
+ flush (cb) {
+ this.inputStream.end(() => {
+ if (!this.handleContentP) {
+ const e = new Error('Cache input stream was empty')
+ e.code = 'ENODATA'
+ // empty streams are probably emitting end right away.
+ // defer this one tick by rejecting a promise on it.
+ return Promise.reject(e).catch(cb)
+ }
+ this.handleContentP.then(
+ (res) => {
+ res.integrity && this.emit('integrity', res.integrity)
+ res.size !== null && this.emit('size', res.size)
+ cb()
+ },
+ (er) => cb(er)
+ )
+ })
+ }
+}
+
+function writeStream (cache, opts = {}) {
+ return new CacacheWriteStream(cache, opts)
+}
+
+function handleContent (inputStream, cache, opts) {
+ return disposer(makeTmp(cache, opts), makeTmpDisposer, (tmp) => {
+ return pipeToTmp(inputStream, cache, tmp.target, opts)
+ .then((res) => {
+ return moveToDestination(
+ tmp,
+ cache,
+ res.integrity,
+ opts
+ ).then(() => res)
+ })
+ })
+}
+
+function pipeToTmp (inputStream, cache, tmpTarget, opts) {
+ let integrity
+ let size
+ const hashStream = ssri.integrityStream({
+ integrity: opts.integrity,
+ algorithms: opts.algorithms,
+ size: opts.size,
+ })
+ hashStream.on('integrity', i => {
+ integrity = i
+ })
+ hashStream.on('size', s => {
+ size = s
+ })
+
+ const outStream = new fsm.WriteStream(tmpTarget, {
+ flags: 'wx',
+ })
+
+ // NB: this can throw if the hashStream has a problem with
+ // it, and the data is fully written. but pipeToTmp is only
+ // called in promisory contexts where that is handled.
+ const pipeline = new Pipeline(
+ inputStream,
+ hashStream,
+ outStream
+ )
+
+ return pipeline.promise()
+ .then(() => ({ integrity, size }))
+ .catch(er => rimraf(tmpTarget).then(() => {
+ throw er
+ }))
+}
+
+function makeTmp (cache, opts) {
+ const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
+ return fixOwner.mkdirfix(cache, path.dirname(tmpTarget)).then(() => ({
+ target: tmpTarget,
+ moved: false,
+ }))
+}
+
+function makeTmpDisposer (tmp) {
+ if (tmp.moved)
+ return Promise.resolve()
+
+ return rimraf(tmp.target)
+}
+
+function moveToDestination (tmp, cache, sri, opts) {
+ const destination = contentPath(cache, sri)
+ const destDir = path.dirname(destination)
+
+ return fixOwner
+ .mkdirfix(cache, destDir)
+ .then(() => {
+ return moveFile(tmp.target, destination)
+ })
+ .then(() => {
+ tmp.moved = true
+ return fixOwner.chownr(cache, destination)
+ })
+}
+
+function sizeError (expected, found) {
+ const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
+ err.expected = expected
+ err.found = found
+ err.code = 'EBADSIZE'
+ return err
+}
+
+function checksumError (expected, found) {
+ const err = new Error(`Integrity check failed:
+ Wanted: ${expected}
+ Found: ${found}`)
+ err.code = 'EINTEGRITY'
+ err.expected = expected
+ err.found = found
+ return err
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/entry-index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/entry-index.js
new file mode 100644
index 000000000..71aac5ed7
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/entry-index.js
@@ -0,0 +1,394 @@
+'use strict'
+
+const util = require('util')
+const crypto = require('crypto')
+const fs = require('fs')
+const Minipass = require('minipass')
+const path = require('path')
+const ssri = require('ssri')
+const uniqueFilename = require('unique-filename')
+
+const { disposer } = require('./util/disposer')
+const contentPath = require('./content/path')
+const fixOwner = require('./util/fix-owner')
+const hashToSegments = require('./util/hash-to-segments')
+const indexV = require('../package.json')['cache-version'].index
+const moveFile = require('@npmcli/move-file')
+const _rimraf = require('rimraf')
+const rimraf = util.promisify(_rimraf)
+rimraf.sync = _rimraf.sync
+
+const appendFile = util.promisify(fs.appendFile)
+const readFile = util.promisify(fs.readFile)
+const readdir = util.promisify(fs.readdir)
+const writeFile = util.promisify(fs.writeFile)
+
+module.exports.NotFoundError = class NotFoundError extends Error {
+ constructor (cache, key) {
+ super(`No cache entry for ${key} found in ${cache}`)
+ this.code = 'ENOENT'
+ this.cache = cache
+ this.key = key
+ }
+}
+
+module.exports.compact = compact
+
+async function compact (cache, key, matchFn, opts = {}) {
+ const bucket = bucketPath(cache, key)
+ const entries = await bucketEntries(bucket)
+ const newEntries = []
+ // we loop backwards because the bottom-most result is the newest
+ // since we add new entries with appendFile
+ for (let i = entries.length - 1; i >= 0; --i) {
+ const entry = entries[i]
+ // a null integrity could mean either a delete was appended
+ // or the user has simply stored an index that does not map
+ // to any content. we determine if the user wants to keep the
+ // null integrity based on the validateEntry function passed in options.
+ // if the integrity is null and no validateEntry is provided, we break
+ // as we consider the null integrity to be a deletion of everything
+ // that came before it.
+ if (entry.integrity === null && !opts.validateEntry)
+ break
+
+ // if this entry is valid, and it is either the first entry or
+ // the newEntries array doesn't already include an entry that
+ // matches this one based on the provided matchFn, then we add
+ // it to the beginning of our list
+ if ((!opts.validateEntry || opts.validateEntry(entry) === true) &&
+ (newEntries.length === 0 ||
+ !newEntries.find((oldEntry) => matchFn(oldEntry, entry))))
+ newEntries.unshift(entry)
+ }
+
+ const newIndex = '\n' + newEntries.map((entry) => {
+ const stringified = JSON.stringify(entry)
+ const hash = hashEntry(stringified)
+ return `${hash}\t${stringified}`
+ }).join('\n')
+
+ const setup = async () => {
+ const target = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
+ await fixOwner.mkdirfix(cache, path.dirname(target))
+ return {
+ target,
+ moved: false,
+ }
+ }
+
+ const teardown = async (tmp) => {
+ if (!tmp.moved)
+ return rimraf(tmp.target)
+ }
+
+ const write = async (tmp) => {
+ await writeFile(tmp.target, newIndex, { flag: 'wx' })
+ await fixOwner.mkdirfix(cache, path.dirname(bucket))
+ // we use @npmcli/move-file directly here because we
+ // want to overwrite the existing file
+ await moveFile(tmp.target, bucket)
+ tmp.moved = true
+ try {
+ await fixOwner.chownr(cache, bucket)
+ } catch (err) {
+ if (err.code !== 'ENOENT')
+ throw err
+ }
+ }
+
+ // write the file atomically
+ await disposer(setup(), teardown, write)
+
+ // we reverse the list we generated such that the newest
+ // entries come first in order to make looping through them easier
+ // the true passed to formatEntry tells it to keep null
+ // integrity values, if they made it this far it's because
+ // validateEntry returned true, and as such we should return it
+ return newEntries.reverse().map((entry) => formatEntry(cache, entry, true))
+}
+
+module.exports.insert = insert
+
+function insert (cache, key, integrity, opts = {}) {
+ const { metadata, size } = opts
+ const bucket = bucketPath(cache, key)
+ const entry = {
+ key,
+ integrity: integrity && ssri.stringify(integrity),
+ time: Date.now(),
+ size,
+ metadata,
+ }
+ return fixOwner
+ .mkdirfix(cache, path.dirname(bucket))
+ .then(() => {
+ const stringified = JSON.stringify(entry)
+ // NOTE - Cleverness ahoy!
+ //
+ // This works because it's tremendously unlikely for an entry to corrupt
+ // another while still preserving the string length of the JSON in
+ // question. So, we just slap the length in there and verify it on read.
+ //
+ // Thanks to @isaacs for the whiteboarding session that ended up with
+ // this.
+ return appendFile(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
+ })
+ .then(() => fixOwner.chownr(cache, bucket))
+ .catch((err) => {
+ if (err.code === 'ENOENT')
+ return undefined
+
+ throw err
+ // There's a class of race conditions that happen when things get deleted
+ // during fixOwner, or between the two mkdirfix/chownr calls.
+ //
+ // It's perfectly fine to just not bother in those cases and lie
+ // that the index entry was written. Because it's a cache.
+ })
+ .then(() => {
+ return formatEntry(cache, entry)
+ })
+}
+
+module.exports.insert.sync = insertSync
+
+function insertSync (cache, key, integrity, opts = {}) {
+ const { metadata, size } = opts
+ const bucket = bucketPath(cache, key)
+ const entry = {
+ key,
+ integrity: integrity && ssri.stringify(integrity),
+ time: Date.now(),
+ size,
+ metadata,
+ }
+ fixOwner.mkdirfix.sync(cache, path.dirname(bucket))
+ const stringified = JSON.stringify(entry)
+ fs.appendFileSync(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
+ try {
+ fixOwner.chownr.sync(cache, bucket)
+ } catch (err) {
+ if (err.code !== 'ENOENT')
+ throw err
+ }
+ return formatEntry(cache, entry)
+}
+
+module.exports.find = find
+
+function find (cache, key) {
+ const bucket = bucketPath(cache, key)
+ return bucketEntries(bucket)
+ .then((entries) => {
+ return entries.reduce((latest, next) => {
+ if (next && next.key === key)
+ return formatEntry(cache, next)
+ else
+ return latest
+ }, null)
+ })
+ .catch((err) => {
+ if (err.code === 'ENOENT')
+ return null
+ else
+ throw err
+ })
+}
+
+module.exports.find.sync = findSync
+
+function findSync (cache, key) {
+ const bucket = bucketPath(cache, key)
+ try {
+ return bucketEntriesSync(bucket).reduce((latest, next) => {
+ if (next && next.key === key)
+ return formatEntry(cache, next)
+ else
+ return latest
+ }, null)
+ } catch (err) {
+ if (err.code === 'ENOENT')
+ return null
+ else
+ throw err
+ }
+}
+
+module.exports.delete = del
+
+function del (cache, key, opts = {}) {
+ if (!opts.removeFully)
+ return insert(cache, key, null, opts)
+
+ const bucket = bucketPath(cache, key)
+ return rimraf(bucket)
+}
+
+module.exports.delete.sync = delSync
+
+function delSync (cache, key, opts = {}) {
+ if (!opts.removeFully)
+ return insertSync(cache, key, null, opts)
+
+ const bucket = bucketPath(cache, key)
+ return rimraf.sync(bucket)
+}
+
+module.exports.lsStream = lsStream
+
+function lsStream (cache) {
+ const indexDir = bucketDir(cache)
+ const stream = new Minipass({ objectMode: true })
+
+ readdirOrEmpty(indexDir).then(buckets => Promise.all(
+ buckets.map(bucket => {
+ const bucketPath = path.join(indexDir, bucket)
+ return readdirOrEmpty(bucketPath).then(subbuckets => Promise.all(
+ subbuckets.map(subbucket => {
+ const subbucketPath = path.join(bucketPath, subbucket)
+
+ // "/cachename//./*"
+ return readdirOrEmpty(subbucketPath).then(entries => Promise.all(
+ entries.map(entry => {
+ const entryPath = path.join(subbucketPath, entry)
+ return bucketEntries(entryPath).then(entries =>
+ // using a Map here prevents duplicate keys from
+ // showing up twice, I guess?
+ entries.reduce((acc, entry) => {
+ acc.set(entry.key, entry)
+ return acc
+ }, new Map())
+ ).then(reduced => {
+ // reduced is a map of key => entry
+ for (const entry of reduced.values()) {
+ const formatted = formatEntry(cache, entry)
+ if (formatted)
+ stream.write(formatted)
+ }
+ }).catch(err => {
+ if (err.code === 'ENOENT')
+ return undefined
+ throw err
+ })
+ })
+ ))
+ })
+ ))
+ })
+ ))
+ .then(
+ () => stream.end(),
+ err => stream.emit('error', err)
+ )
+
+ return stream
+}
+
+module.exports.ls = ls
+
+function ls (cache) {
+ return lsStream(cache).collect().then(entries =>
+ entries.reduce((acc, xs) => {
+ acc[xs.key] = xs
+ return acc
+ }, {})
+ )
+}
+
+module.exports.bucketEntries = bucketEntries
+
+function bucketEntries (bucket, filter) {
+ return readFile(bucket, 'utf8').then((data) => _bucketEntries(data, filter))
+}
+
+module.exports.bucketEntries.sync = bucketEntriesSync
+
+function bucketEntriesSync (bucket, filter) {
+ const data = fs.readFileSync(bucket, 'utf8')
+ return _bucketEntries(data, filter)
+}
+
+function _bucketEntries (data, filter) {
+ const entries = []
+ data.split('\n').forEach((entry) => {
+ if (!entry)
+ return
+
+ const pieces = entry.split('\t')
+ if (!pieces[1] || hashEntry(pieces[1]) !== pieces[0]) {
+ // Hash is no good! Corruption or malice? Doesn't matter!
+ // EJECT EJECT
+ return
+ }
+ let obj
+ try {
+ obj = JSON.parse(pieces[1])
+ } catch (e) {
+ // Entry is corrupted!
+ return
+ }
+ if (obj)
+ entries.push(obj)
+ })
+ return entries
+}
+
+module.exports.bucketDir = bucketDir
+
+function bucketDir (cache) {
+ return path.join(cache, `index-v${indexV}`)
+}
+
+module.exports.bucketPath = bucketPath
+
+function bucketPath (cache, key) {
+ const hashed = hashKey(key)
+ return path.join.apply(
+ path,
+ [bucketDir(cache)].concat(hashToSegments(hashed))
+ )
+}
+
+module.exports.hashKey = hashKey
+
+function hashKey (key) {
+ return hash(key, 'sha256')
+}
+
+module.exports.hashEntry = hashEntry
+
+function hashEntry (str) {
+ return hash(str, 'sha1')
+}
+
+function hash (str, digest) {
+ return crypto
+ .createHash(digest)
+ .update(str)
+ .digest('hex')
+}
+
+function formatEntry (cache, entry, keepAll) {
+ // Treat null digests as deletions. They'll shadow any previous entries.
+ if (!entry.integrity && !keepAll)
+ return null
+
+ return {
+ key: entry.key,
+ integrity: entry.integrity,
+ path: entry.integrity ? contentPath(cache, entry.integrity) : undefined,
+ size: entry.size,
+ time: entry.time,
+ metadata: entry.metadata,
+ }
+}
+
+function readdirOrEmpty (dir) {
+ return readdir(dir).catch((err) => {
+ if (err.code === 'ENOENT' || err.code === 'ENOTDIR')
+ return []
+
+ throw err
+ })
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/memoization.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/memoization.js
new file mode 100644
index 000000000..d5465f39f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/memoization.js
@@ -0,0 +1,73 @@
+'use strict'
+
+const LRU = require('lru-cache')
+
+const MAX_SIZE = 50 * 1024 * 1024 // 50MB
+const MAX_AGE = 3 * 60 * 1000
+
+const MEMOIZED = new LRU({
+ max: MAX_SIZE,
+ maxAge: MAX_AGE,
+ length: (entry, key) => key.startsWith('key:') ? entry.data.length : entry.length,
+})
+
+module.exports.clearMemoized = clearMemoized
+
+function clearMemoized () {
+ const old = {}
+ MEMOIZED.forEach((v, k) => {
+ old[k] = v
+ })
+ MEMOIZED.reset()
+ return old
+}
+
+module.exports.put = put
+
+function put (cache, entry, data, opts) {
+ pickMem(opts).set(`key:${cache}:${entry.key}`, { entry, data })
+ putDigest(cache, entry.integrity, data, opts)
+}
+
+module.exports.put.byDigest = putDigest
+
+function putDigest (cache, integrity, data, opts) {
+ pickMem(opts).set(`digest:${cache}:${integrity}`, data)
+}
+
+module.exports.get = get
+
+function get (cache, key, opts) {
+ return pickMem(opts).get(`key:${cache}:${key}`)
+}
+
+module.exports.get.byDigest = getDigest
+
+function getDigest (cache, integrity, opts) {
+ return pickMem(opts).get(`digest:${cache}:${integrity}`)
+}
+
+class ObjProxy {
+ constructor (obj) {
+ this.obj = obj
+ }
+
+ get (key) {
+ return this.obj[key]
+ }
+
+ set (key, val) {
+ this.obj[key] = val
+ }
+}
+
+function pickMem (opts) {
+ if (!opts || !opts.memoize)
+ return MEMOIZED
+ else if (opts.memoize.get && opts.memoize.set)
+ return opts.memoize
+ else if (typeof opts.memoize === 'object')
+ return new ObjProxy(opts.memoize)
+ else
+ return MEMOIZED
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/disposer.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/disposer.js
new file mode 100644
index 000000000..aa8aed54d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/disposer.js
@@ -0,0 +1,30 @@
+'use strict'
+
+module.exports.disposer = disposer
+
+function disposer (creatorFn, disposerFn, fn) {
+ const runDisposer = (resource, result, shouldThrow = false) => {
+ return disposerFn(resource)
+ .then(
+ // disposer resolved, do something with original fn's promise
+ () => {
+ if (shouldThrow)
+ throw result
+
+ return result
+ },
+ // Disposer fn failed, crash process
+ (err) => {
+ throw err
+ // Or process.exit?
+ })
+ }
+
+ return creatorFn
+ .then((resource) => {
+ // fn(resource) can throw, so wrap in a promise here
+ return Promise.resolve().then(() => fn(resource))
+ .then((result) => runDisposer(resource, result))
+ .catch((err) => runDisposer(resource, err, true))
+ })
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/fix-owner.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/fix-owner.js
new file mode 100644
index 000000000..90ffece52
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/fix-owner.js
@@ -0,0 +1,142 @@
+'use strict'
+
+const util = require('util')
+
+const chownr = util.promisify(require('chownr'))
+const mkdirp = require('mkdirp')
+const inflight = require('promise-inflight')
+const inferOwner = require('infer-owner')
+
+// Memoize getuid()/getgid() calls.
+// patch process.setuid/setgid to invalidate cached value on change
+const self = { uid: null, gid: null }
+const getSelf = () => {
+ if (typeof self.uid !== 'number') {
+ self.uid = process.getuid()
+ const setuid = process.setuid
+ process.setuid = (uid) => {
+ self.uid = null
+ process.setuid = setuid
+ return process.setuid(uid)
+ }
+ }
+ if (typeof self.gid !== 'number') {
+ self.gid = process.getgid()
+ const setgid = process.setgid
+ process.setgid = (gid) => {
+ self.gid = null
+ process.setgid = setgid
+ return process.setgid(gid)
+ }
+ }
+}
+
+module.exports.chownr = fixOwner
+
+function fixOwner (cache, filepath) {
+ if (!process.getuid) {
+ // This platform doesn't need ownership fixing
+ return Promise.resolve()
+ }
+
+ getSelf()
+ if (self.uid !== 0) {
+ // almost certainly can't chown anyway
+ return Promise.resolve()
+ }
+
+ return Promise.resolve(inferOwner(cache)).then((owner) => {
+ const { uid, gid } = owner
+
+ // No need to override if it's already what we used.
+ if (self.uid === uid && self.gid === gid)
+ return
+
+ return inflight('fixOwner: fixing ownership on ' + filepath, () =>
+ chownr(
+ filepath,
+ typeof uid === 'number' ? uid : self.uid,
+ typeof gid === 'number' ? gid : self.gid
+ ).catch((err) => {
+ if (err.code === 'ENOENT')
+ return null
+
+ throw err
+ })
+ )
+ })
+}
+
+module.exports.chownr.sync = fixOwnerSync
+
+function fixOwnerSync (cache, filepath) {
+ if (!process.getuid) {
+ // This platform doesn't need ownership fixing
+ return
+ }
+ const { uid, gid } = inferOwner.sync(cache)
+ getSelf()
+ if (self.uid !== 0) {
+ // almost certainly can't chown anyway
+ return
+ }
+
+ if (self.uid === uid && self.gid === gid) {
+ // No need to override if it's already what we used.
+ return
+ }
+ try {
+ chownr.sync(
+ filepath,
+ typeof uid === 'number' ? uid : self.uid,
+ typeof gid === 'number' ? gid : self.gid
+ )
+ } catch (err) {
+ // only catch ENOENT, any other error is a problem.
+ if (err.code === 'ENOENT')
+ return null
+
+ throw err
+ }
+}
+
+module.exports.mkdirfix = mkdirfix
+
+function mkdirfix (cache, p, cb) {
+ // we have to infer the owner _before_ making the directory, even though
+ // we aren't going to use the results, since the cache itself might not
+ // exist yet. If we mkdirp it, then our current uid/gid will be assumed
+ // to be correct if it creates the cache folder in the process.
+ return Promise.resolve(inferOwner(cache)).then(() => {
+ return mkdirp(p)
+ .then((made) => {
+ if (made)
+ return fixOwner(cache, made).then(() => made)
+ })
+ .catch((err) => {
+ if (err.code === 'EEXIST')
+ return fixOwner(cache, p).then(() => null)
+
+ throw err
+ })
+ })
+}
+
+module.exports.mkdirfix.sync = mkdirfixSync
+
+function mkdirfixSync (cache, p) {
+ try {
+ inferOwner.sync(cache)
+ const made = mkdirp.sync(p)
+ if (made) {
+ fixOwnerSync(cache, made)
+ return made
+ }
+ } catch (err) {
+ if (err.code === 'EEXIST') {
+ fixOwnerSync(cache, p)
+ return null
+ } else
+ throw err
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/hash-to-segments.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/hash-to-segments.js
new file mode 100644
index 000000000..445599b50
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/hash-to-segments.js
@@ -0,0 +1,7 @@
+'use strict'
+
+module.exports = hashToSegments
+
+function hashToSegments (hash) {
+ return [hash.slice(0, 2), hash.slice(2, 4), hash.slice(4)]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/move-file.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/move-file.js
new file mode 100644
index 000000000..c3f9e35eb
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/move-file.js
@@ -0,0 +1,67 @@
+'use strict'
+
+const fs = require('fs')
+const util = require('util')
+const chmod = util.promisify(fs.chmod)
+const unlink = util.promisify(fs.unlink)
+const stat = util.promisify(fs.stat)
+const move = require('@npmcli/move-file')
+const pinflight = require('promise-inflight')
+
+module.exports = moveFile
+
+function moveFile (src, dest) {
+ const isWindows = global.__CACACHE_TEST_FAKE_WINDOWS__ ||
+ process.platform === 'win32'
+
+ // This isn't quite an fs.rename -- the assumption is that
+ // if `dest` already exists, and we get certain errors while
+ // trying to move it, we should just not bother.
+ //
+ // In the case of cache corruption, users will receive an
+ // EINTEGRITY error elsewhere, and can remove the offending
+ // content their own way.
+ //
+ // Note that, as the name suggests, this strictly only supports file moves.
+ return new Promise((resolve, reject) => {
+ fs.link(src, dest, (err) => {
+ if (err) {
+ if (isWindows && err.code === 'EPERM') {
+ // XXX This is a really weird way to handle this situation, as it
+ // results in the src file being deleted even though the dest
+ // might not exist. Since we pretty much always write files to
+ // deterministic locations based on content hash, this is likely
+ // ok (or at worst, just ends in a future cache miss). But it would
+ // be worth investigating at some time in the future if this is
+ // really what we want to do here.
+ return resolve()
+ } else if (err.code === 'EEXIST' || err.code === 'EBUSY') {
+ // file already exists, so whatever
+ return resolve()
+ } else
+ return reject(err)
+ } else
+ return resolve()
+ })
+ })
+ .then(() => {
+ // content should never change for any reason, so make it read-only
+ return Promise.all([
+ unlink(src),
+ !isWindows && chmod(dest, '0444'),
+ ])
+ })
+ .catch(() => {
+ return pinflight('cacache-move-file:' + dest, () => {
+ return stat(dest).catch((err) => {
+ if (err.code !== 'ENOENT') {
+ // Something else is wrong here. Bail bail bail
+ throw err
+ }
+ // file doesn't already exist! let's try a rename -> copy fallback
+ // only delete if it successfully copies
+ return move(src, dest)
+ })
+ })
+ })
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/tmp.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/tmp.js
new file mode 100644
index 000000000..0a5a50eba
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/util/tmp.js
@@ -0,0 +1,35 @@
+'use strict'
+
+const fs = require('@npmcli/fs')
+
+const fixOwner = require('./fix-owner')
+const path = require('path')
+
+module.exports.mkdir = mktmpdir
+
+function mktmpdir (cache, opts = {}) {
+ const { tmpPrefix } = opts
+ const tmpDir = path.join(cache, 'tmp')
+ return fs.mkdir(tmpDir, { recursive: true, owner: 'inherit' })
+ .then(() => {
+ // do not use path.join(), it drops the trailing / if tmpPrefix is unset
+ const target = `${tmpDir}${path.sep}${tmpPrefix || ''}`
+ return fs.mkdtemp(target, { owner: 'inherit' })
+ })
+}
+
+module.exports.withTmp = withTmp
+
+function withTmp (cache, opts, cb) {
+ if (!cb) {
+ cb = opts
+ opts = {}
+ }
+ return fs.withTempDir(path.join(cache, 'tmp'), cb, opts)
+}
+
+module.exports.fix = fixtmpdir
+
+function fixtmpdir (cache) {
+ return fixOwner(cache, path.join(cache, 'tmp'))
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/verify.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/verify.js
new file mode 100644
index 000000000..e9d679ece
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/lib/verify.js
@@ -0,0 +1,287 @@
+'use strict'
+
+const util = require('util')
+
+const pMap = require('p-map')
+const contentPath = require('./content/path')
+const fixOwner = require('./util/fix-owner')
+const fs = require('fs')
+const fsm = require('fs-minipass')
+const glob = util.promisify(require('glob'))
+const index = require('./entry-index')
+const path = require('path')
+const rimraf = util.promisify(require('rimraf'))
+const ssri = require('ssri')
+
+const hasOwnProperty = (obj, key) =>
+ Object.prototype.hasOwnProperty.call(obj, key)
+
+const stat = util.promisify(fs.stat)
+const truncate = util.promisify(fs.truncate)
+const writeFile = util.promisify(fs.writeFile)
+const readFile = util.promisify(fs.readFile)
+
+const verifyOpts = (opts) => ({
+ concurrency: 20,
+ log: { silly () {} },
+ ...opts,
+})
+
+module.exports = verify
+
+function verify (cache, opts) {
+ opts = verifyOpts(opts)
+ opts.log.silly('verify', 'verifying cache at', cache)
+
+ const steps = [
+ markStartTime,
+ fixPerms,
+ garbageCollect,
+ rebuildIndex,
+ cleanTmp,
+ writeVerifile,
+ markEndTime,
+ ]
+
+ return steps
+ .reduce((promise, step, i) => {
+ const label = step.name
+ const start = new Date()
+ return promise.then((stats) => {
+ return step(cache, opts).then((s) => {
+ s &&
+ Object.keys(s).forEach((k) => {
+ stats[k] = s[k]
+ })
+ const end = new Date()
+ if (!stats.runTime)
+ stats.runTime = {}
+
+ stats.runTime[label] = end - start
+ return Promise.resolve(stats)
+ })
+ })
+ }, Promise.resolve({}))
+ .then((stats) => {
+ stats.runTime.total = stats.endTime - stats.startTime
+ opts.log.silly(
+ 'verify',
+ 'verification finished for',
+ cache,
+ 'in',
+ `${stats.runTime.total}ms`
+ )
+ return stats
+ })
+}
+
+function markStartTime (cache, opts) {
+ return Promise.resolve({ startTime: new Date() })
+}
+
+function markEndTime (cache, opts) {
+ return Promise.resolve({ endTime: new Date() })
+}
+
+function fixPerms (cache, opts) {
+ opts.log.silly('verify', 'fixing cache permissions')
+ return fixOwner
+ .mkdirfix(cache, cache)
+ .then(() => {
+ // TODO - fix file permissions too
+ return fixOwner.chownr(cache, cache)
+ })
+ .then(() => null)
+}
+
+// Implements a naive mark-and-sweep tracing garbage collector.
+//
+// The algorithm is basically as follows:
+// 1. Read (and filter) all index entries ("pointers")
+// 2. Mark each integrity value as "live"
+// 3. Read entire filesystem tree in `content-vX/` dir
+// 4. If content is live, verify its checksum and delete it if it fails
+// 5. If content is not marked as live, rimraf it.
+//
+function garbageCollect (cache, opts) {
+ opts.log.silly('verify', 'garbage collecting content')
+ const indexStream = index.lsStream(cache)
+ const liveContent = new Set()
+ indexStream.on('data', (entry) => {
+ if (opts.filter && !opts.filter(entry))
+ return
+
+ liveContent.add(entry.integrity.toString())
+ })
+ return new Promise((resolve, reject) => {
+ indexStream.on('end', resolve).on('error', reject)
+ }).then(() => {
+ const contentDir = contentPath.contentDir(cache)
+ return glob(path.join(contentDir, '**'), {
+ follow: false,
+ nodir: true,
+ nosort: true,
+ }).then((files) => {
+ return Promise.resolve({
+ verifiedContent: 0,
+ reclaimedCount: 0,
+ reclaimedSize: 0,
+ badContentCount: 0,
+ keptSize: 0,
+ }).then((stats) =>
+ pMap(
+ files,
+ (f) => {
+ const split = f.split(/[/\\]/)
+ const digest = split.slice(split.length - 3).join('')
+ const algo = split[split.length - 4]
+ const integrity = ssri.fromHex(digest, algo)
+ if (liveContent.has(integrity.toString())) {
+ return verifyContent(f, integrity).then((info) => {
+ if (!info.valid) {
+ stats.reclaimedCount++
+ stats.badContentCount++
+ stats.reclaimedSize += info.size
+ } else {
+ stats.verifiedContent++
+ stats.keptSize += info.size
+ }
+ return stats
+ })
+ } else {
+ // No entries refer to this content. We can delete.
+ stats.reclaimedCount++
+ return stat(f).then((s) => {
+ return rimraf(f).then(() => {
+ stats.reclaimedSize += s.size
+ return stats
+ })
+ })
+ }
+ },
+ { concurrency: opts.concurrency }
+ ).then(() => stats)
+ )
+ })
+ })
+}
+
+function verifyContent (filepath, sri) {
+ return stat(filepath)
+ .then((s) => {
+ const contentInfo = {
+ size: s.size,
+ valid: true,
+ }
+ return ssri
+ .checkStream(new fsm.ReadStream(filepath), sri)
+ .catch((err) => {
+ if (err.code !== 'EINTEGRITY')
+ throw err
+
+ return rimraf(filepath).then(() => {
+ contentInfo.valid = false
+ })
+ })
+ .then(() => contentInfo)
+ })
+ .catch((err) => {
+ if (err.code === 'ENOENT')
+ return { size: 0, valid: false }
+
+ throw err
+ })
+}
+
+function rebuildIndex (cache, opts) {
+ opts.log.silly('verify', 'rebuilding index')
+ return index.ls(cache).then((entries) => {
+ const stats = {
+ missingContent: 0,
+ rejectedEntries: 0,
+ totalEntries: 0,
+ }
+ const buckets = {}
+ for (const k in entries) {
+ /* istanbul ignore else */
+ if (hasOwnProperty(entries, k)) {
+ const hashed = index.hashKey(k)
+ const entry = entries[k]
+ const excluded = opts.filter && !opts.filter(entry)
+ excluded && stats.rejectedEntries++
+ if (buckets[hashed] && !excluded)
+ buckets[hashed].push(entry)
+ else if (buckets[hashed] && excluded) {
+ // skip
+ } else if (excluded) {
+ buckets[hashed] = []
+ buckets[hashed]._path = index.bucketPath(cache, k)
+ } else {
+ buckets[hashed] = [entry]
+ buckets[hashed]._path = index.bucketPath(cache, k)
+ }
+ }
+ }
+ return pMap(
+ Object.keys(buckets),
+ (key) => {
+ return rebuildBucket(cache, buckets[key], stats, opts)
+ },
+ { concurrency: opts.concurrency }
+ ).then(() => stats)
+ })
+}
+
+function rebuildBucket (cache, bucket, stats, opts) {
+ return truncate(bucket._path).then(() => {
+ // This needs to be serialized because cacache explicitly
+ // lets very racy bucket conflicts clobber each other.
+ return bucket.reduce((promise, entry) => {
+ return promise.then(() => {
+ const content = contentPath(cache, entry.integrity)
+ return stat(content)
+ .then(() => {
+ return index
+ .insert(cache, entry.key, entry.integrity, {
+ metadata: entry.metadata,
+ size: entry.size,
+ })
+ .then(() => {
+ stats.totalEntries++
+ })
+ })
+ .catch((err) => {
+ if (err.code === 'ENOENT') {
+ stats.rejectedEntries++
+ stats.missingContent++
+ return
+ }
+ throw err
+ })
+ })
+ }, Promise.resolve())
+ })
+}
+
+function cleanTmp (cache, opts) {
+ opts.log.silly('verify', 'cleaning tmp directory')
+ return rimraf(path.join(cache, 'tmp'))
+}
+
+function writeVerifile (cache, opts) {
+ const verifile = path.join(cache, '_lastverified')
+ opts.log.silly('verify', 'writing verifile to ' + verifile)
+ try {
+ return writeFile(verifile, '' + +new Date())
+ } finally {
+ fixOwner.chownr.sync(cache, verifile)
+ }
+}
+
+module.exports.lastRun = lastRun
+
+function lastRun (cache) {
+ return readFile(path.join(cache, '_lastverified'), 'utf8').then(
+ (data) => new Date(+data)
+ )
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/ls.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/ls.js
new file mode 100644
index 000000000..6006c99e3
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/ls.js
@@ -0,0 +1,6 @@
+'use strict'
+
+const index = require('./lib/entry-index')
+
+module.exports = index.ls
+module.exports.stream = index.lsStream
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/package.json
new file mode 100644
index 000000000..6cb414015
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/package.json
@@ -0,0 +1,80 @@
+{
+ "name": "cacache",
+ "version": "15.3.0",
+ "cache-version": {
+ "content": "2",
+ "index": "5"
+ },
+ "description": "Fast, fault-tolerant, cross-platform, disk-based, data-agnostic, content-addressable cache.",
+ "main": "index.js",
+ "files": [
+ "*.js",
+ "lib"
+ ],
+ "scripts": {
+ "benchmarks": "node test/benchmarks",
+ "preversion": "npm test",
+ "postversion": "npm publish",
+ "prepublishOnly": "git push origin --follow-tags",
+ "test": "tap",
+ "snap": "tap",
+ "coverage": "tap",
+ "test-docker": "docker run -it --rm --name pacotest -v \"$PWD\":/tmp -w /tmp node:latest npm test",
+ "lint": "npm run npmclilint -- \"*.*js\" \"lib/**/*.*js\" \"test/**/*.*js\"",
+ "npmclilint": "npmcli-lint",
+ "lintfix": "npm run lint -- --fix",
+ "postsnap": "npm run lintfix --"
+ },
+ "repository": "https://github.com/npm/cacache",
+ "keywords": [
+ "cache",
+ "caching",
+ "content-addressable",
+ "sri",
+ "sri hash",
+ "subresource integrity",
+ "cache",
+ "storage",
+ "store",
+ "file store",
+ "filesystem",
+ "disk cache",
+ "disk storage"
+ ],
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^1.0.0",
+ "@npmcli/move-file": "^1.0.1",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "glob": "^7.1.4",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.1",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.2",
+ "mkdirp": "^1.0.3",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^8.0.1",
+ "tar": "^6.0.2",
+ "unique-filename": "^1.1.1"
+ },
+ "devDependencies": {
+ "@npmcli/lint": "^1.0.1",
+ "benchmark": "^2.1.4",
+ "chalk": "^4.0.0",
+ "require-inject": "^1.4.4",
+ "tacks": "^1.3.0",
+ "tap": "^15.0.9"
+ },
+ "tap": {
+ "100": true,
+ "test-regex": "test/[^/]*.js"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/put.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/put.js
new file mode 100644
index 000000000..84e9562bc
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/put.js
@@ -0,0 +1,83 @@
+'use strict'
+
+const index = require('./lib/entry-index')
+const memo = require('./lib/memoization')
+const write = require('./lib/content/write')
+const Flush = require('minipass-flush')
+const { PassThrough } = require('minipass-collect')
+const Pipeline = require('minipass-pipeline')
+
+const putOpts = (opts) => ({
+ algorithms: ['sha512'],
+ ...opts,
+})
+
+module.exports = putData
+
+function putData (cache, key, data, opts = {}) {
+ const { memoize } = opts
+ opts = putOpts(opts)
+ return write(cache, data, opts).then((res) => {
+ return index
+ .insert(cache, key, res.integrity, { ...opts, size: res.size })
+ .then((entry) => {
+ if (memoize)
+ memo.put(cache, entry, data, opts)
+
+ return res.integrity
+ })
+ })
+}
+
+module.exports.stream = putStream
+
+function putStream (cache, key, opts = {}) {
+ const { memoize } = opts
+ opts = putOpts(opts)
+ let integrity
+ let size
+
+ let memoData
+ const pipeline = new Pipeline()
+ // first item in the pipeline is the memoizer, because we need
+ // that to end first and get the collected data.
+ if (memoize) {
+ const memoizer = new PassThrough().on('collect', data => {
+ memoData = data
+ })
+ pipeline.push(memoizer)
+ }
+
+ // contentStream is a write-only, not a passthrough
+ // no data comes out of it.
+ const contentStream = write.stream(cache, opts)
+ .on('integrity', (int) => {
+ integrity = int
+ })
+ .on('size', (s) => {
+ size = s
+ })
+
+ pipeline.push(contentStream)
+
+ // last but not least, we write the index and emit hash and size,
+ // and memoize if we're doing that
+ pipeline.push(new Flush({
+ flush () {
+ return index
+ .insert(cache, key, integrity, { ...opts, size })
+ .then((entry) => {
+ if (memoize && memoData)
+ memo.put(cache, entry, memoData, opts)
+
+ if (integrity)
+ pipeline.emit('integrity', integrity)
+
+ if (size)
+ pipeline.emit('size', size)
+ })
+ },
+ }))
+
+ return pipeline
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/rm.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/rm.js
new file mode 100644
index 000000000..f2ef6b190
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/rm.js
@@ -0,0 +1,31 @@
+'use strict'
+
+const util = require('util')
+
+const index = require('./lib/entry-index')
+const memo = require('./lib/memoization')
+const path = require('path')
+const rimraf = util.promisify(require('rimraf'))
+const rmContent = require('./lib/content/rm')
+
+module.exports = entry
+module.exports.entry = entry
+
+function entry (cache, key, opts) {
+ memo.clearMemoized()
+ return index.delete(cache, key, opts)
+}
+
+module.exports.content = content
+
+function content (cache, integrity) {
+ memo.clearMemoized()
+ return rmContent(cache, integrity)
+}
+
+module.exports.all = all
+
+function all (cache) {
+ memo.clearMemoized()
+ return rimraf(path.join(cache, '*(content-*|index-*)'))
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/verify.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/verify.js
new file mode 100644
index 000000000..db7763d7a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/cacache/verify.js
@@ -0,0 +1,3 @@
+'use strict'
+
+module.exports = require('./lib/verify')
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.eslintrc b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.eslintrc
new file mode 100644
index 000000000..201e859be
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.eslintrc
@@ -0,0 +1,17 @@
+{
+ "root": true,
+
+ "extends": "@ljharb",
+
+ "rules": {
+ "func-name-matching": 0,
+ "id-length": 0,
+ "new-cap": [2, {
+ "capIsNewExceptions": [
+ "GetIntrinsic",
+ ],
+ }],
+ "no-extra-parens": 0,
+ "no-magic-numbers": 0,
+ },
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.github/FUNDING.yml b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.github/FUNDING.yml
new file mode 100644
index 000000000..0011e9d65
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: [ljharb]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: npm/call-bind-apply-helpers
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.nycrc b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.nycrc
new file mode 100644
index 000000000..bdd626ce9
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/.nycrc
@@ -0,0 +1,9 @@
+{
+ "all": true,
+ "check-coverage": false,
+ "reporter": ["text-summary", "text", "html", "json"],
+ "exclude": [
+ "coverage",
+ "test"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/CHANGELOG.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/CHANGELOG.md
new file mode 100644
index 000000000..24849428b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/CHANGELOG.md
@@ -0,0 +1,30 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [v1.0.2](https://github.com/ljharb/call-bind-apply-helpers/compare/v1.0.1...v1.0.2) - 2025-02-12
+
+### Commits
+
+- [types] improve inferred types [`e6f9586`](https://github.com/ljharb/call-bind-apply-helpers/commit/e6f95860a3c72879cb861a858cdfb8138fbedec1)
+- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/tsconfig`, `@types/tape`, `es-value-fixtures`, `for-each`, `has-strict-mode`, `object-inspect` [`e43d540`](https://github.com/ljharb/call-bind-apply-helpers/commit/e43d5409f97543bfbb11f345d47d8ce4e066d8c1)
+
+## [v1.0.1](https://github.com/ljharb/call-bind-apply-helpers/compare/v1.0.0...v1.0.1) - 2024-12-08
+
+### Commits
+
+- [types] `reflectApply`: fix types [`4efc396`](https://github.com/ljharb/call-bind-apply-helpers/commit/4efc3965351a4f02cc55e836fa391d3d11ef2ef8)
+- [Fix] `reflectApply`: oops, Reflect is not a function [`83cc739`](https://github.com/ljharb/call-bind-apply-helpers/commit/83cc7395de6b79b7730bdf092f1436f0b1263c75)
+- [Dev Deps] update `@arethetypeswrong/cli` [`80bd5d3`](https://github.com/ljharb/call-bind-apply-helpers/commit/80bd5d3ae58b4f6b6995ce439dd5a1bcb178a940)
+
+## v1.0.0 - 2024-12-05
+
+### Commits
+
+- Initial implementation, tests, readme [`7879629`](https://github.com/ljharb/call-bind-apply-helpers/commit/78796290f9b7430c9934d6f33d94ae9bc89fce04)
+- Initial commit [`3f1dc16`](https://github.com/ljharb/call-bind-apply-helpers/commit/3f1dc164afc43285631b114a5f9dd9137b2b952f)
+- npm init [`081df04`](https://github.com/ljharb/call-bind-apply-helpers/commit/081df048c312fcee400922026f6e97281200a603)
+- Only apps should have lockfiles [`5b9ca0f`](https://github.com/ljharb/call-bind-apply-helpers/commit/5b9ca0fe8101ebfaf309c549caac4e0a017ed930)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/LICENSE
new file mode 100644
index 000000000..f82f38963
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Jordan Harband
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/README.md
new file mode 100644
index 000000000..8fc0dae1b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/README.md
@@ -0,0 +1,62 @@
+# call-bind-apply-helpers [![Version Badge][npm-version-svg]][package-url]
+
+[![github actions][actions-image]][actions-url]
+[![coverage][codecov-image]][codecov-url]
+[![dependency status][deps-svg]][deps-url]
+[![dev dependency status][dev-deps-svg]][dev-deps-url]
+[![License][license-image]][license-url]
+[![Downloads][downloads-image]][downloads-url]
+
+[![npm badge][npm-badge-png]][package-url]
+
+Helper functions around Function call/apply/bind, for use in `call-bind`.
+
+The only packages that should likely ever use this package directly are `call-bind` and `get-intrinsic`.
+Please use `call-bind` unless you have a very good reason not to.
+
+## Getting started
+
+```sh
+npm install --save call-bind-apply-helpers
+```
+
+## Usage/Examples
+
+```js
+const assert = require('assert');
+const callBindBasic = require('call-bind-apply-helpers');
+
+function f(a, b) {
+ assert.equal(this, 1);
+ assert.equal(a, 2);
+ assert.equal(b, 3);
+ assert.equal(arguments.length, 2);
+}
+
+const fBound = callBindBasic([f, 1]);
+
+delete Function.prototype.call;
+delete Function.prototype.bind;
+
+fBound(2, 3);
+```
+
+## Tests
+
+Clone the repo, `npm install`, and run `npm test`
+
+[package-url]: https://npmjs.org/package/call-bind-apply-helpers
+[npm-version-svg]: https://versionbadg.es/ljharb/call-bind-apply-helpers.svg
+[deps-svg]: https://david-dm.org/ljharb/call-bind-apply-helpers.svg
+[deps-url]: https://david-dm.org/ljharb/call-bind-apply-helpers
+[dev-deps-svg]: https://david-dm.org/ljharb/call-bind-apply-helpers/dev-status.svg
+[dev-deps-url]: https://david-dm.org/ljharb/call-bind-apply-helpers#info=devDependencies
+[npm-badge-png]: https://nodei.co/npm/call-bind-apply-helpers.png?downloads=true&stars=true
+[license-image]: https://img.shields.io/npm/l/call-bind-apply-helpers.svg
+[license-url]: LICENSE
+[downloads-image]: https://img.shields.io/npm/dm/call-bind-apply-helpers.svg
+[downloads-url]: https://npm-stat.com/charts.html?package=call-bind-apply-helpers
+[codecov-image]: https://codecov.io/gh/ljharb/call-bind-apply-helpers/branch/main/graphs/badge.svg
+[codecov-url]: https://app.codecov.io/gh/ljharb/call-bind-apply-helpers/
+[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/call-bind-apply-helpers
+[actions-url]: https://github.com/ljharb/call-bind-apply-helpers/actions
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/actualApply.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/actualApply.d.ts
new file mode 100644
index 000000000..b87286a21
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/actualApply.d.ts
@@ -0,0 +1 @@
+export = Reflect.apply;
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/actualApply.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/actualApply.js
new file mode 100644
index 000000000..ffa51355d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/actualApply.js
@@ -0,0 +1,10 @@
+'use strict';
+
+var bind = require('function-bind');
+
+var $apply = require('./functionApply');
+var $call = require('./functionCall');
+var $reflectApply = require('./reflectApply');
+
+/** @type {import('./actualApply')} */
+module.exports = $reflectApply || bind.call($call, $apply);
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/applyBind.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/applyBind.d.ts
new file mode 100644
index 000000000..d176c1ab3
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/applyBind.d.ts
@@ -0,0 +1,19 @@
+import actualApply from './actualApply';
+
+type TupleSplitHead = T['length'] extends N
+ ? T
+ : T extends [...infer R, any]
+ ? TupleSplitHead
+ : never
+
+type TupleSplitTail = O['length'] extends N
+ ? T
+ : T extends [infer F, ...infer R]
+ ? TupleSplitTail<[...R], N, [...O, F]>
+ : never
+
+type TupleSplit = [TupleSplitHead, TupleSplitTail]
+
+declare function applyBind(...args: TupleSplit, 2>[1]): ReturnType;
+
+export = applyBind;
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/applyBind.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/applyBind.js
new file mode 100644
index 000000000..d2b772314
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/applyBind.js
@@ -0,0 +1,10 @@
+'use strict';
+
+var bind = require('function-bind');
+var $apply = require('./functionApply');
+var actualApply = require('./actualApply');
+
+/** @type {import('./applyBind')} */
+module.exports = function applyBind() {
+ return actualApply(bind, $apply, arguments);
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionApply.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionApply.d.ts
new file mode 100644
index 000000000..1f6e11b3d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionApply.d.ts
@@ -0,0 +1 @@
+export = Function.prototype.apply;
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionApply.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionApply.js
new file mode 100644
index 000000000..c71df9c2b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionApply.js
@@ -0,0 +1,4 @@
+'use strict';
+
+/** @type {import('./functionApply')} */
+module.exports = Function.prototype.apply;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionCall.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionCall.d.ts
new file mode 100644
index 000000000..15e93df35
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionCall.d.ts
@@ -0,0 +1 @@
+export = Function.prototype.call;
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionCall.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionCall.js
new file mode 100644
index 000000000..7a8d87357
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/functionCall.js
@@ -0,0 +1,4 @@
+'use strict';
+
+/** @type {import('./functionCall')} */
+module.exports = Function.prototype.call;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/index.d.ts
new file mode 100644
index 000000000..541516bd0
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/index.d.ts
@@ -0,0 +1,64 @@
+type RemoveFromTuple<
+ Tuple extends readonly unknown[],
+ RemoveCount extends number,
+ Index extends 1[] = []
+> = Index["length"] extends RemoveCount
+ ? Tuple
+ : Tuple extends [infer First, ...infer Rest]
+ ? RemoveFromTuple
+ : Tuple;
+
+type ConcatTuples<
+ Prefix extends readonly unknown[],
+ Suffix extends readonly unknown[]
+> = [...Prefix, ...Suffix];
+
+type ExtractFunctionParams = T extends (this: infer TThis, ...args: infer P extends readonly unknown[]) => infer R
+ ? { thisArg: TThis; params: P; returnType: R }
+ : never;
+
+type BindFunction<
+ T extends (this: any, ...args: any[]) => any,
+ TThis,
+ TBoundArgs extends readonly unknown[],
+ ReceiverBound extends boolean
+> = ExtractFunctionParams extends {
+ thisArg: infer OrigThis;
+ params: infer P extends readonly unknown[];
+ returnType: infer R;
+}
+ ? ReceiverBound extends true
+ ? (...args: RemoveFromTuple>) => R extends [OrigThis, ...infer Rest]
+ ? [TThis, ...Rest] // Replace `this` with `thisArg`
+ : R
+ : >>(
+ thisArg: U,
+ ...args: RemainingArgs
+ ) => R extends [OrigThis, ...infer Rest]
+ ? [U, ...ConcatTuples] // Preserve bound args in return type
+ : R
+ : never;
+
+declare function callBind<
+ const T extends (this: any, ...args: any[]) => any,
+ Extracted extends ExtractFunctionParams,
+ const TBoundArgs extends Partial & readonly unknown[],
+ const TThis extends Extracted["thisArg"]
+>(
+ args: [fn: T, thisArg: TThis, ...boundArgs: TBoundArgs]
+): BindFunction;
+
+declare function callBind<
+ const T extends (this: any, ...args: any[]) => any,
+ Extracted extends ExtractFunctionParams,
+ const TBoundArgs extends Partial & readonly unknown[]
+>(
+ args: [fn: T, ...boundArgs: TBoundArgs]
+): BindFunction;
+
+declare function callBind(
+ args: [fn: Exclude, ...rest: TArgs]
+): never;
+
+// export as namespace callBind;
+export = callBind;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/index.js
new file mode 100644
index 000000000..2f6dab4c1
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/index.js
@@ -0,0 +1,15 @@
+'use strict';
+
+var bind = require('function-bind');
+var $TypeError = require('es-errors/type');
+
+var $call = require('./functionCall');
+var $actualApply = require('./actualApply');
+
+/** @type {(args: [Function, thisArg?: unknown, ...args: unknown[]]) => Function} TODO FIXME, find a way to use import('.') */
+module.exports = function callBindBasic(args) {
+ if (args.length < 1 || typeof args[0] !== 'function') {
+ throw new $TypeError('a function is required');
+ }
+ return $actualApply(bind, $call, args);
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/package.json
new file mode 100644
index 000000000..923b8be2f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/package.json
@@ -0,0 +1,85 @@
+{
+ "name": "call-bind-apply-helpers",
+ "version": "1.0.2",
+ "description": "Helper functions around Function call/apply/bind, for use in `call-bind`",
+ "main": "index.js",
+ "exports": {
+ ".": "./index.js",
+ "./actualApply": "./actualApply.js",
+ "./applyBind": "./applyBind.js",
+ "./functionApply": "./functionApply.js",
+ "./functionCall": "./functionCall.js",
+ "./reflectApply": "./reflectApply.js",
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "prepack": "npmignore --auto --commentLines=auto",
+ "prepublish": "not-in-publish || npm run prepublishOnly",
+ "prepublishOnly": "safe-publish-latest",
+ "prelint": "evalmd README.md",
+ "lint": "eslint --ext=.js,.mjs .",
+ "postlint": "tsc -p . && attw -P",
+ "pretest": "npm run lint",
+ "tests-only": "nyc tape 'test/**/*.js'",
+ "test": "npm run tests-only",
+ "posttest": "npx npm@'>=10.2' audit --production",
+ "version": "auto-changelog && git add CHANGELOG.md",
+ "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/ljharb/call-bind-apply-helpers.git"
+ },
+ "author": "Jordan Harband ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/ljharb/call-bind-apply-helpers/issues"
+ },
+ "homepage": "https://github.com/ljharb/call-bind-apply-helpers#readme",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "devDependencies": {
+ "@arethetypeswrong/cli": "^0.17.3",
+ "@ljharb/eslint-config": "^21.1.1",
+ "@ljharb/tsconfig": "^0.2.3",
+ "@types/for-each": "^0.3.3",
+ "@types/function-bind": "^1.1.10",
+ "@types/object-inspect": "^1.13.0",
+ "@types/tape": "^5.8.1",
+ "auto-changelog": "^2.5.0",
+ "encoding": "^0.1.13",
+ "es-value-fixtures": "^1.7.1",
+ "eslint": "=8.8.0",
+ "evalmd": "^0.0.19",
+ "for-each": "^0.3.5",
+ "has-strict-mode": "^1.1.0",
+ "in-publish": "^2.0.1",
+ "npmignore": "^0.3.1",
+ "nyc": "^10.3.2",
+ "object-inspect": "^1.13.4",
+ "safe-publish-latest": "^2.0.0",
+ "tape": "^5.9.0",
+ "typescript": "next"
+ },
+ "testling": {
+ "files": "test/index.js"
+ },
+ "auto-changelog": {
+ "output": "CHANGELOG.md",
+ "template": "keepachangelog",
+ "unreleased": false,
+ "commitLimit": false,
+ "backfillLimit": false,
+ "hideCredit": true
+ },
+ "publishConfig": {
+ "ignore": [
+ ".github/workflows"
+ ]
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/reflectApply.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/reflectApply.d.ts
new file mode 100644
index 000000000..6b2ae764c
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/reflectApply.d.ts
@@ -0,0 +1,3 @@
+declare const reflectApply: false | typeof Reflect.apply;
+
+export = reflectApply;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/reflectApply.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/reflectApply.js
new file mode 100644
index 000000000..3d03caa69
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/reflectApply.js
@@ -0,0 +1,4 @@
+'use strict';
+
+/** @type {import('./reflectApply')} */
+module.exports = typeof Reflect !== 'undefined' && Reflect && Reflect.apply;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/test/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/test/index.js
new file mode 100644
index 000000000..1cdc89ed4
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/test/index.js
@@ -0,0 +1,63 @@
+'use strict';
+
+var callBind = require('../');
+var hasStrictMode = require('has-strict-mode')();
+var forEach = require('for-each');
+var inspect = require('object-inspect');
+var v = require('es-value-fixtures');
+
+var test = require('tape');
+
+test('callBindBasic', function (t) {
+ forEach(v.nonFunctions, function (nonFunction) {
+ t['throws'](
+ // @ts-expect-error
+ function () { callBind([nonFunction]); },
+ TypeError,
+ inspect(nonFunction) + ' is not a function'
+ );
+ });
+
+ var sentinel = { sentinel: true };
+ /** @type {(this: T, a: A, b: B) => [T | undefined, A, B]} */
+ var func = function (a, b) {
+ // eslint-disable-next-line no-invalid-this
+ return [!hasStrictMode && this === global ? undefined : this, a, b];
+ };
+ t.equal(func.length, 2, 'original function length is 2');
+
+ /** type {(thisArg: unknown, a: number, b: number) => [unknown, number, number]} */
+ var bound = callBind([func]);
+ /** type {((a: number, b: number) => [typeof sentinel, typeof a, typeof b])} */
+ var boundR = callBind([func, sentinel]);
+ /** type {((b: number) => [typeof sentinel, number, typeof b])} */
+ var boundArg = callBind([func, sentinel, /** @type {const} */ (1)]);
+
+ // @ts-expect-error
+ t.deepEqual(bound(), [undefined, undefined, undefined], 'bound func with no args');
+
+ // @ts-expect-error
+ t.deepEqual(func(), [undefined, undefined, undefined], 'unbound func with too few args');
+ // @ts-expect-error
+ t.deepEqual(bound(1, 2), [hasStrictMode ? 1 : Object(1), 2, undefined], 'bound func too few args');
+ // @ts-expect-error
+ t.deepEqual(boundR(), [sentinel, undefined, undefined], 'bound func with receiver, with too few args');
+ // @ts-expect-error
+ t.deepEqual(boundArg(), [sentinel, 1, undefined], 'bound func with receiver and arg, with too few args');
+
+ t.deepEqual(func(1, 2), [undefined, 1, 2], 'unbound func with right args');
+ t.deepEqual(bound(1, 2, 3), [hasStrictMode ? 1 : Object(1), 2, 3], 'bound func with right args');
+ t.deepEqual(boundR(1, 2), [sentinel, 1, 2], 'bound func with receiver, with right args');
+ t.deepEqual(boundArg(2), [sentinel, 1, 2], 'bound func with receiver and arg, with right arg');
+
+ // @ts-expect-error
+ t.deepEqual(func(1, 2, 3), [undefined, 1, 2], 'unbound func with too many args');
+ // @ts-expect-error
+ t.deepEqual(bound(1, 2, 3, 4), [hasStrictMode ? 1 : Object(1), 2, 3], 'bound func with too many args');
+ // @ts-expect-error
+ t.deepEqual(boundR(1, 2, 3), [sentinel, 1, 2], 'bound func with receiver, with too many args');
+ // @ts-expect-error
+ t.deepEqual(boundArg(2, 3), [sentinel, 1, 2], 'bound func with receiver and arg, with too many args');
+
+ t.end();
+});
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/tsconfig.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/tsconfig.json
new file mode 100644
index 000000000..aef999308
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bind-apply-helpers/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@ljharb/tsconfig",
+ "compilerOptions": {
+ "target": "es2021",
+ },
+ "exclude": [
+ "coverage",
+ ],
+}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.eslintrc b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.eslintrc
new file mode 100644
index 000000000..2612ed8fe
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.eslintrc
@@ -0,0 +1,13 @@
+{
+ "root": true,
+
+ "extends": "@ljharb",
+
+ "rules": {
+ "new-cap": [2, {
+ "capIsNewExceptions": [
+ "GetIntrinsic",
+ ],
+ }],
+ },
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.github/FUNDING.yml b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.github/FUNDING.yml
new file mode 100644
index 000000000..2a2a13571
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: [ljharb]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: npm/call-bound
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.nycrc b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.nycrc
new file mode 100644
index 000000000..bdd626ce9
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/.nycrc
@@ -0,0 +1,9 @@
+{
+ "all": true,
+ "check-coverage": false,
+ "reporter": ["text-summary", "text", "html", "json"],
+ "exclude": [
+ "coverage",
+ "test"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/CHANGELOG.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/CHANGELOG.md
new file mode 100644
index 000000000..8bde4e9a5
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/CHANGELOG.md
@@ -0,0 +1,42 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [v1.0.4](https://github.com/ljharb/call-bound/compare/v1.0.3...v1.0.4) - 2025-03-03
+
+### Commits
+
+- [types] improve types [`e648922`](https://github.com/ljharb/call-bound/commit/e6489222a9e54f350fbf952ceabe51fd8b6027ff)
+- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/tsconfig`, `@types/tape`, `es-value-fixtures`, `for-each`, `has-strict-mode`, `object-inspect` [`a42a5eb`](https://github.com/ljharb/call-bound/commit/a42a5ebe6c1b54fcdc7997c7dc64fdca9e936719)
+- [Deps] update `call-bind-apply-helpers`, `get-intrinsic` [`f529eac`](https://github.com/ljharb/call-bound/commit/f529eac132404c17156bbc23ab2297a25d0f20b8)
+
+## [v1.0.3](https://github.com/ljharb/call-bound/compare/v1.0.2...v1.0.3) - 2024-12-15
+
+### Commits
+
+- [Refactor] use `call-bind-apply-helpers` instead of `call-bind` [`5e0b134`](https://github.com/ljharb/call-bound/commit/5e0b13496df14fb7d05dae9412f088da8d3f75be)
+- [Deps] update `get-intrinsic` [`41fc967`](https://github.com/ljharb/call-bound/commit/41fc96732a22c7b7e8f381f93ccc54bb6293be2e)
+- [readme] fix example [`79a0137`](https://github.com/ljharb/call-bound/commit/79a0137723f7c6d09c9c05452bbf8d5efb5d6e49)
+- [meta] add `sideEffects` flag [`08b07be`](https://github.com/ljharb/call-bound/commit/08b07be7f1c03f67dc6f3cdaf0906259771859f7)
+
+## [v1.0.2](https://github.com/ljharb/call-bound/compare/v1.0.1...v1.0.2) - 2024-12-10
+
+### Commits
+
+- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/tsconfig`, `gopd` [`e6a5ffe`](https://github.com/ljharb/call-bound/commit/e6a5ffe849368fe4f74dfd6cdeca1b9baa39e8d5)
+- [Deps] update `call-bind`, `get-intrinsic` [`2aeb5b5`](https://github.com/ljharb/call-bound/commit/2aeb5b521dc2b2683d1345c753ea1161de2d1c14)
+- [types] improve return type [`1a0c9fe`](https://github.com/ljharb/call-bound/commit/1a0c9fe3114471e7ca1f57d104e2efe713bb4871)
+
+## v1.0.1 - 2024-12-05
+
+### Commits
+
+- Initial implementation, tests, readme, types [`6d94121`](https://github.com/ljharb/call-bound/commit/6d94121a9243602e506334069f7a03189fe3363d)
+- Initial commit [`0eae867`](https://github.com/ljharb/call-bound/commit/0eae867334ea025c33e6e91cdecfc9df96680cf9)
+- npm init [`71b2479`](https://github.com/ljharb/call-bound/commit/71b2479c6723e0b7d91a6b663613067e98b7b275)
+- Only apps should have lockfiles [`c3754a9`](https://github.com/ljharb/call-bound/commit/c3754a949b7f9132b47e2d18c1729889736741eb)
+- [actions] skip `npm ls` in node < 10 [`74275a5`](https://github.com/ljharb/call-bound/commit/74275a5186b8caf6309b6b97472bdcb0df4683a8)
+- [Dev Deps] add missing peer dep [`1354de8`](https://github.com/ljharb/call-bound/commit/1354de8679413e4ae9c523d85f76fa7a5e032d97)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/LICENSE
new file mode 100644
index 000000000..f82f38963
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Jordan Harband
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/README.md
new file mode 100644
index 000000000..a44e43e56
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/README.md
@@ -0,0 +1,53 @@
+# call-bound [![Version Badge][npm-version-svg]][package-url]
+
+[![github actions][actions-image]][actions-url]
+[![coverage][codecov-image]][codecov-url]
+[![dependency status][deps-svg]][deps-url]
+[![dev dependency status][dev-deps-svg]][dev-deps-url]
+[![License][license-image]][license-url]
+[![Downloads][downloads-image]][downloads-url]
+
+[![npm badge][npm-badge-png]][package-url]
+
+Robust call-bound JavaScript intrinsics, using `call-bind` and `get-intrinsic`.
+
+## Getting started
+
+```sh
+npm install --save call-bound
+```
+
+## Usage/Examples
+
+```js
+const assert = require('assert');
+const callBound = require('call-bound');
+
+const slice = callBound('Array.prototype.slice');
+
+delete Function.prototype.call;
+delete Function.prototype.bind;
+delete Array.prototype.slice;
+
+assert.deepEqual(slice([1, 2, 3, 4], 1, -1), [2, 3]);
+```
+
+## Tests
+
+Clone the repo, `npm install`, and run `npm test`
+
+[package-url]: https://npmjs.org/package/call-bound
+[npm-version-svg]: https://versionbadg.es/ljharb/call-bound.svg
+[deps-svg]: https://david-dm.org/ljharb/call-bound.svg
+[deps-url]: https://david-dm.org/ljharb/call-bound
+[dev-deps-svg]: https://david-dm.org/ljharb/call-bound/dev-status.svg
+[dev-deps-url]: https://david-dm.org/ljharb/call-bound#info=devDependencies
+[npm-badge-png]: https://nodei.co/npm/call-bound.png?downloads=true&stars=true
+[license-image]: https://img.shields.io/npm/l/call-bound.svg
+[license-url]: LICENSE
+[downloads-image]: https://img.shields.io/npm/dm/call-bound.svg
+[downloads-url]: https://npm-stat.com/charts.html?package=call-bound
+[codecov-image]: https://codecov.io/gh/ljharb/call-bound/branch/main/graphs/badge.svg
+[codecov-url]: https://app.codecov.io/gh/ljharb/call-bound/
+[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/call-bound
+[actions-url]: https://github.com/ljharb/call-bound/actions
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/index.d.ts
new file mode 100644
index 000000000..5562f00ed
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/index.d.ts
@@ -0,0 +1,94 @@
+type Intrinsic = typeof globalThis;
+
+type IntrinsicName = keyof Intrinsic | `%${keyof Intrinsic}%`;
+
+type IntrinsicPath = IntrinsicName | `${StripPercents}.${string}` | `%${StripPercents}.${string}%`;
+
+type AllowMissing = boolean;
+
+type StripPercents = T extends `%${infer U}%` ? U : T;
+
+type BindMethodPrecise =
+ F extends (this: infer This, ...args: infer Args) => infer R
+ ? (obj: This, ...args: Args) => R
+ : F extends {
+ (this: infer This1, ...args: infer Args1): infer R1;
+ (this: infer This2, ...args: infer Args2): infer R2
+ }
+ ? {
+ (obj: This1, ...args: Args1): R1;
+ (obj: This2, ...args: Args2): R2
+ }
+ : never
+
+// Extract method type from a prototype
+type GetPrototypeMethod =
+ (typeof globalThis)[T] extends { prototype: any }
+ ? M extends keyof (typeof globalThis)[T]['prototype']
+ ? (typeof globalThis)[T]['prototype'][M]
+ : never
+ : never
+
+// Get static property/method
+type GetStaticMember =
+ P extends keyof (typeof globalThis)[T] ? (typeof globalThis)[T][P] : never
+
+// Type that maps string path to actual bound function or value with better precision
+type BoundIntrinsic =
+ S extends `${infer Obj}.prototype.${infer Method}`
+ ? Obj extends keyof typeof globalThis
+ ? BindMethodPrecise>
+ : unknown
+ : S extends `${infer Obj}.${infer Prop}`
+ ? Obj extends keyof typeof globalThis
+ ? GetStaticMember
+ : unknown
+ : unknown
+
+declare function arraySlice(array: readonly T[], start?: number, end?: number): T[];
+declare function arraySlice(array: ArrayLike, start?: number, end?: number): T[];
+declare function arraySlice(array: IArguments, start?: number, end?: number): T[];
+
+// Special cases for methods that need explicit typing
+interface SpecialCases {
+ '%Object.prototype.isPrototypeOf%': (thisArg: {}, obj: unknown) => boolean;
+ '%String.prototype.replace%': {
+ (str: string, searchValue: string | RegExp, replaceValue: string): string;
+ (str: string, searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string
+ };
+ '%Object.prototype.toString%': (obj: {}) => string;
+ '%Object.prototype.hasOwnProperty%': (obj: {}, v: PropertyKey) => boolean;
+ '%Array.prototype.slice%': typeof arraySlice;
+ '%Array.prototype.map%': (array: readonly T[], callbackfn: (value: T, index: number, array: readonly T[]) => U, thisArg?: any) => U[];
+ '%Array.prototype.filter%': (array: readonly T[], predicate: (value: T, index: number, array: readonly T[]) => unknown, thisArg?: any) => T[];
+ '%Array.prototype.indexOf%': (array: readonly T[], searchElement: T, fromIndex?: number) => number;
+ '%Function.prototype.apply%': (fn: (...args: A) => R, thisArg: any, args: A) => R;
+ '%Function.prototype.call%': (fn: (...args: A) => R, thisArg: any, ...args: A) => R;
+ '%Function.prototype.bind%': (fn: (...args: A) => R, thisArg: any, ...args: A) => (...remainingArgs: A) => R;
+ '%Promise.prototype.then%': {
+ (promise: Promise, onfulfilled: (value: T) => R | PromiseLike): Promise;
+ (promise: Promise, onfulfilled: ((value: T) => R | PromiseLike) | undefined | null, onrejected: (reason: any) => R | PromiseLike): Promise;
+ };
+ '%RegExp.prototype.test%': (regexp: RegExp, str: string) => boolean;
+ '%RegExp.prototype.exec%': (regexp: RegExp, str: string) => RegExpExecArray | null;
+ '%Error.prototype.toString%': (error: Error) => string;
+ '%TypeError.prototype.toString%': (error: TypeError) => string;
+ '%String.prototype.split%': (
+ obj: unknown,
+ splitter: string | RegExp | {
+ [Symbol.split](string: string, limit?: number): string[];
+ },
+ limit?: number | undefined
+ ) => string[];
+}
+
+/**
+ * Returns a bound function for a prototype method, or a value for a static property.
+ *
+ * @param name - The name of the intrinsic (e.g. 'Array.prototype.slice')
+ * @param {AllowMissing} [allowMissing] - Whether to allow missing intrinsics (default: false)
+ */
+declare function callBound, S extends IntrinsicPath>(name: K, allowMissing?: AllowMissing): SpecialCases[`%${StripPercents}%`];
+declare function callBound, S extends IntrinsicPath>(name: S, allowMissing?: AllowMissing): BoundIntrinsic;
+
+export = callBound;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/index.js
new file mode 100644
index 000000000..e9ade749d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/index.js
@@ -0,0 +1,19 @@
+'use strict';
+
+var GetIntrinsic = require('get-intrinsic');
+
+var callBindBasic = require('call-bind-apply-helpers');
+
+/** @type {(thisArg: string, searchString: string, position?: number) => number} */
+var $indexOf = callBindBasic([GetIntrinsic('%String.prototype.indexOf%')]);
+
+/** @type {import('.')} */
+module.exports = function callBoundIntrinsic(name, allowMissing) {
+ /* eslint no-extra-parens: 0 */
+
+ var intrinsic = /** @type {(this: unknown, ...args: unknown[]) => unknown} */ (GetIntrinsic(name, !!allowMissing));
+ if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) {
+ return callBindBasic(/** @type {const} */ ([intrinsic]));
+ }
+ return intrinsic;
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/package.json
new file mode 100644
index 000000000..d542db430
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/package.json
@@ -0,0 +1,99 @@
+{
+ "name": "call-bound",
+ "version": "1.0.4",
+ "description": "Robust call-bound JavaScript intrinsics, using `call-bind` and `get-intrinsic`.",
+ "main": "index.js",
+ "exports": {
+ ".": "./index.js",
+ "./package.json": "./package.json"
+ },
+ "sideEffects": false,
+ "scripts": {
+ "prepack": "npmignore --auto --commentLines=auto",
+ "prepublish": "not-in-publish || npm run prepublishOnly",
+ "prepublishOnly": "safe-publish-latest",
+ "prelint": "evalmd README.md",
+ "lint": "eslint --ext=.js,.mjs .",
+ "postlint": "tsc -p . && attw -P",
+ "pretest": "npm run lint",
+ "tests-only": "nyc tape 'test/**/*.js'",
+ "test": "npm run tests-only",
+ "posttest": "npx npm@'>=10.2' audit --production",
+ "version": "auto-changelog && git add CHANGELOG.md",
+ "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/ljharb/call-bound.git"
+ },
+ "keywords": [
+ "javascript",
+ "ecmascript",
+ "es",
+ "js",
+ "callbind",
+ "callbound",
+ "call",
+ "bind",
+ "bound",
+ "call-bind",
+ "call-bound",
+ "function",
+ "es-abstract"
+ ],
+ "author": "Jordan Harband ",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/ljharb/call-bound/issues"
+ },
+ "homepage": "https://github.com/ljharb/call-bound#readme",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "devDependencies": {
+ "@arethetypeswrong/cli": "^0.17.4",
+ "@ljharb/eslint-config": "^21.1.1",
+ "@ljharb/tsconfig": "^0.3.0",
+ "@types/call-bind": "^1.0.5",
+ "@types/get-intrinsic": "^1.2.3",
+ "@types/tape": "^5.8.1",
+ "auto-changelog": "^2.5.0",
+ "encoding": "^0.1.13",
+ "es-value-fixtures": "^1.7.1",
+ "eslint": "=8.8.0",
+ "evalmd": "^0.0.19",
+ "for-each": "^0.3.5",
+ "gopd": "^1.2.0",
+ "has-strict-mode": "^1.1.0",
+ "in-publish": "^2.0.1",
+ "npmignore": "^0.3.1",
+ "nyc": "^10.3.2",
+ "object-inspect": "^1.13.4",
+ "safe-publish-latest": "^2.0.0",
+ "tape": "^5.9.0",
+ "typescript": "next"
+ },
+ "testling": {
+ "files": "test/index.js"
+ },
+ "auto-changelog": {
+ "output": "CHANGELOG.md",
+ "template": "keepachangelog",
+ "unreleased": false,
+ "commitLimit": false,
+ "backfillLimit": false,
+ "hideCredit": true
+ },
+ "publishConfig": {
+ "ignore": [
+ ".github/workflows"
+ ]
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/test/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/test/index.js
new file mode 100644
index 000000000..a2fc9f0f2
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/test/index.js
@@ -0,0 +1,61 @@
+'use strict';
+
+var test = require('tape');
+
+var callBound = require('../');
+
+/** @template {true} T @template U @typedef {T extends U ? T : never} AssertType */
+
+test('callBound', function (t) {
+ // static primitive
+ t.equal(callBound('Array.length'), Array.length, 'Array.length yields itself');
+ t.equal(callBound('%Array.length%'), Array.length, '%Array.length% yields itself');
+
+ // static non-function object
+ t.equal(callBound('Array.prototype'), Array.prototype, 'Array.prototype yields itself');
+ t.equal(callBound('%Array.prototype%'), Array.prototype, '%Array.prototype% yields itself');
+ t.equal(callBound('Array.constructor'), Array.constructor, 'Array.constructor yields itself');
+ t.equal(callBound('%Array.constructor%'), Array.constructor, '%Array.constructor% yields itself');
+
+ // static function
+ t.equal(callBound('Date.parse'), Date.parse, 'Date.parse yields itself');
+ t.equal(callBound('%Date.parse%'), Date.parse, '%Date.parse% yields itself');
+
+ // prototype primitive
+ t.equal(callBound('Error.prototype.message'), Error.prototype.message, 'Error.prototype.message yields itself');
+ t.equal(callBound('%Error.prototype.message%'), Error.prototype.message, '%Error.prototype.message% yields itself');
+
+ var x = callBound('Object.prototype.toString');
+ var y = callBound('%Object.prototype.toString%');
+
+ // prototype function
+ t.notEqual(x, Object.prototype.toString, 'Object.prototype.toString does not yield itself');
+ t.notEqual(y, Object.prototype.toString, '%Object.prototype.toString% does not yield itself');
+ t.equal(x(true), Object.prototype.toString.call(true), 'call-bound Object.prototype.toString calls into the original');
+ t.equal(y(true), Object.prototype.toString.call(true), 'call-bound %Object.prototype.toString% calls into the original');
+
+ t['throws'](
+ // @ts-expect-error
+ function () { callBound('does not exist'); },
+ SyntaxError,
+ 'nonexistent intrinsic throws'
+ );
+ t['throws'](
+ // @ts-expect-error
+ function () { callBound('does not exist', true); },
+ SyntaxError,
+ 'allowMissing arg still throws for unknown intrinsic'
+ );
+
+ t.test('real but absent intrinsic', { skip: typeof WeakRef !== 'undefined' }, function (st) {
+ st['throws'](
+ function () { callBound('WeakRef'); },
+ TypeError,
+ 'real but absent intrinsic throws'
+ );
+ st.equal(callBound('WeakRef', true), undefined, 'allowMissing arg avoids exception');
+ st.end();
+ });
+
+ t.end();
+});
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/tsconfig.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/tsconfig.json
new file mode 100644
index 000000000..8976d98b8
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/call-bound/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "@ljharb/tsconfig",
+ "compilerOptions": {
+ "target": "ESNext",
+ "lib": ["es2024"],
+ },
+ "exclude": [
+ "coverage",
+ ],
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/LICENSE
new file mode 100644
index 000000000..19129e315
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/README.md
new file mode 100644
index 000000000..70e9a54a3
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/README.md
@@ -0,0 +1,3 @@
+Like `chown -R`.
+
+Takes the same arguments as `fs.chown()`
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/chownr.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/chownr.js
new file mode 100644
index 000000000..0d4093216
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/chownr.js
@@ -0,0 +1,167 @@
+'use strict'
+const fs = require('fs')
+const path = require('path')
+
+/* istanbul ignore next */
+const LCHOWN = fs.lchown ? 'lchown' : 'chown'
+/* istanbul ignore next */
+const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync'
+
+/* istanbul ignore next */
+const needEISDIRHandled = fs.lchown &&
+ !process.version.match(/v1[1-9]+\./) &&
+ !process.version.match(/v10\.[6-9]/)
+
+const lchownSync = (path, uid, gid) => {
+ try {
+ return fs[LCHOWNSYNC](path, uid, gid)
+ } catch (er) {
+ if (er.code !== 'ENOENT')
+ throw er
+ }
+}
+
+/* istanbul ignore next */
+const chownSync = (path, uid, gid) => {
+ try {
+ return fs.chownSync(path, uid, gid)
+ } catch (er) {
+ if (er.code !== 'ENOENT')
+ throw er
+ }
+}
+
+/* istanbul ignore next */
+const handleEISDIR =
+ needEISDIRHandled ? (path, uid, gid, cb) => er => {
+ // Node prior to v10 had a very questionable implementation of
+ // fs.lchown, which would always try to call fs.open on a directory
+ // Fall back to fs.chown in those cases.
+ if (!er || er.code !== 'EISDIR')
+ cb(er)
+ else
+ fs.chown(path, uid, gid, cb)
+ }
+ : (_, __, ___, cb) => cb
+
+/* istanbul ignore next */
+const handleEISDirSync =
+ needEISDIRHandled ? (path, uid, gid) => {
+ try {
+ return lchownSync(path, uid, gid)
+ } catch (er) {
+ if (er.code !== 'EISDIR')
+ throw er
+ chownSync(path, uid, gid)
+ }
+ }
+ : (path, uid, gid) => lchownSync(path, uid, gid)
+
+// fs.readdir could only accept an options object as of node v6
+const nodeVersion = process.version
+let readdir = (path, options, cb) => fs.readdir(path, options, cb)
+let readdirSync = (path, options) => fs.readdirSync(path, options)
+/* istanbul ignore next */
+if (/^v4\./.test(nodeVersion))
+ readdir = (path, options, cb) => fs.readdir(path, cb)
+
+const chown = (cpath, uid, gid, cb) => {
+ fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, er => {
+ // Skip ENOENT error
+ cb(er && er.code !== 'ENOENT' ? er : null)
+ }))
+}
+
+const chownrKid = (p, child, uid, gid, cb) => {
+ if (typeof child === 'string')
+ return fs.lstat(path.resolve(p, child), (er, stats) => {
+ // Skip ENOENT error
+ if (er)
+ return cb(er.code !== 'ENOENT' ? er : null)
+ stats.name = child
+ chownrKid(p, stats, uid, gid, cb)
+ })
+
+ if (child.isDirectory()) {
+ chownr(path.resolve(p, child.name), uid, gid, er => {
+ if (er)
+ return cb(er)
+ const cpath = path.resolve(p, child.name)
+ chown(cpath, uid, gid, cb)
+ })
+ } else {
+ const cpath = path.resolve(p, child.name)
+ chown(cpath, uid, gid, cb)
+ }
+}
+
+
+const chownr = (p, uid, gid, cb) => {
+ readdir(p, { withFileTypes: true }, (er, children) => {
+ // any error other than ENOTDIR or ENOTSUP means it's not readable,
+ // or doesn't exist. give up.
+ if (er) {
+ if (er.code === 'ENOENT')
+ return cb()
+ else if (er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP')
+ return cb(er)
+ }
+ if (er || !children.length)
+ return chown(p, uid, gid, cb)
+
+ let len = children.length
+ let errState = null
+ const then = er => {
+ if (errState)
+ return
+ if (er)
+ return cb(errState = er)
+ if (-- len === 0)
+ return chown(p, uid, gid, cb)
+ }
+
+ children.forEach(child => chownrKid(p, child, uid, gid, then))
+ })
+}
+
+const chownrKidSync = (p, child, uid, gid) => {
+ if (typeof child === 'string') {
+ try {
+ const stats = fs.lstatSync(path.resolve(p, child))
+ stats.name = child
+ child = stats
+ } catch (er) {
+ if (er.code === 'ENOENT')
+ return
+ else
+ throw er
+ }
+ }
+
+ if (child.isDirectory())
+ chownrSync(path.resolve(p, child.name), uid, gid)
+
+ handleEISDirSync(path.resolve(p, child.name), uid, gid)
+}
+
+const chownrSync = (p, uid, gid) => {
+ let children
+ try {
+ children = readdirSync(p, { withFileTypes: true })
+ } catch (er) {
+ if (er.code === 'ENOENT')
+ return
+ else if (er.code === 'ENOTDIR' || er.code === 'ENOTSUP')
+ return handleEISDirSync(p, uid, gid)
+ else
+ throw er
+ }
+
+ if (children && children.length)
+ children.forEach(child => chownrKidSync(p, child, uid, gid))
+
+ return handleEISDirSync(p, uid, gid)
+}
+
+module.exports = chownr
+chownr.sync = chownrSync
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/package.json
new file mode 100644
index 000000000..5b0214ca1
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/chownr/package.json
@@ -0,0 +1,32 @@
+{
+ "author": "Isaac Z. Schlueter (http://blog.izs.me/)",
+ "name": "chownr",
+ "description": "like `chown -R`",
+ "version": "2.0.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/chownr.git"
+ },
+ "main": "chownr.js",
+ "files": [
+ "chownr.js"
+ ],
+ "devDependencies": {
+ "mkdirp": "0.3",
+ "rimraf": "^2.7.1",
+ "tap": "^14.10.6"
+ },
+ "tap": {
+ "check-coverage": true
+ },
+ "scripts": {
+ "test": "tap",
+ "preversion": "npm test",
+ "postversion": "npm publish",
+ "prepublishOnly": "git push origin --follow-tags"
+ },
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/index.d.ts
new file mode 100644
index 000000000..ed5899509
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/index.d.ts
@@ -0,0 +1,47 @@
+declare namespace cleanStack {
+ interface Options {
+ /**
+ Prettify the file paths in the stack:
+
+ `/Users/sindresorhus/dev/clean-stack/unicorn.js:2:15` → `~/dev/clean-stack/unicorn.js:2:15`
+
+ @default false
+ */
+ readonly pretty?: boolean;
+ }
+}
+
+/**
+Clean up error stack traces. Removes the mostly unhelpful internal Node.js entries.
+
+@param stack - The `stack` property of an `Error`.
+
+@example
+```
+import cleanStack = require('clean-stack');
+
+const error = new Error('Missing unicorn');
+
+console.log(error.stack);
+
+// Error: Missing unicorn
+// at Object. (/Users/sindresorhus/dev/clean-stack/unicorn.js:2:15)
+// at Module._compile (module.js:409:26)
+// at Object.Module._extensions..js (module.js:416:10)
+// at Module.load (module.js:343:32)
+// at Function.Module._load (module.js:300:12)
+// at Function.Module.runMain (module.js:441:10)
+// at startup (node.js:139:18)
+
+console.log(cleanStack(error.stack));
+
+// Error: Missing unicorn
+// at Object. (/Users/sindresorhus/dev/clean-stack/unicorn.js:2:15)
+```
+*/
+declare function cleanStack(
+ stack: string,
+ options?: cleanStack.Options
+): string;
+
+export = cleanStack;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/index.js
new file mode 100644
index 000000000..8c1dcc4cd
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/index.js
@@ -0,0 +1,40 @@
+'use strict';
+const os = require('os');
+
+const extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/;
+const pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/;
+const homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir();
+
+module.exports = (stack, options) => {
+ options = Object.assign({pretty: false}, options);
+
+ return stack.replace(/\\/g, '/')
+ .split('\n')
+ .filter(line => {
+ const pathMatches = line.match(extractPathRegex);
+ if (pathMatches === null || !pathMatches[1]) {
+ return true;
+ }
+
+ const match = pathMatches[1];
+
+ // Electron
+ if (
+ match.includes('.app/Contents/Resources/electron.asar') ||
+ match.includes('.app/Contents/Resources/default_app.asar')
+ ) {
+ return false;
+ }
+
+ return !pathRegex.test(match);
+ })
+ .filter(line => line.trim() !== '')
+ .map(line => {
+ if (options.pretty) {
+ return line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, '~')));
+ }
+
+ return line;
+ })
+ .join('\n');
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/license b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/license
new file mode 100644
index 000000000..e7af2f771
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/license
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/package.json
new file mode 100644
index 000000000..719fdff55
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "clean-stack",
+ "version": "2.2.0",
+ "description": "Clean up error stack traces",
+ "license": "MIT",
+ "repository": "sindresorhus/clean-stack",
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "scripts": {
+ "test": "xo && ava && tsd"
+ },
+ "files": [
+ "index.js",
+ "index.d.ts"
+ ],
+ "keywords": [
+ "clean",
+ "stack",
+ "trace",
+ "traces",
+ "error",
+ "err",
+ "electron"
+ ],
+ "devDependencies": {
+ "ava": "^1.4.1",
+ "tsd": "^0.7.2",
+ "xo": "^0.24.0"
+ },
+ "browser": {
+ "os": false
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/readme.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/readme.md
new file mode 100644
index 000000000..8d4416046
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/clean-stack/readme.md
@@ -0,0 +1,76 @@
+# clean-stack [](https://travis-ci.org/sindresorhus/clean-stack)
+
+> Clean up error stack traces
+
+Removes the mostly unhelpful internal Node.js entries.
+
+Also works in Electron.
+
+
+## Install
+
+```
+$ npm install clean-stack
+```
+
+
+## Usage
+
+```js
+const cleanStack = require('clean-stack');
+
+const error = new Error('Missing unicorn');
+
+console.log(error.stack);
+/*
+Error: Missing unicorn
+ at Object. (/Users/sindresorhus/dev/clean-stack/unicorn.js:2:15)
+ at Module._compile (module.js:409:26)
+ at Object.Module._extensions..js (module.js:416:10)
+ at Module.load (module.js:343:32)
+ at Function.Module._load (module.js:300:12)
+ at Function.Module.runMain (module.js:441:10)
+ at startup (node.js:139:18)
+*/
+
+console.log(cleanStack(error.stack));
+/*
+Error: Missing unicorn
+ at Object. (/Users/sindresorhus/dev/clean-stack/unicorn.js:2:15)
+*/
+```
+
+
+## API
+
+### cleanStack(stack, [options])
+
+#### stack
+
+Type: `string`
+
+The `stack` property of an `Error`.
+
+#### options
+
+Type: `Object`
+
+##### pretty
+
+Type: `boolean`
+Default: `false`
+
+Prettify the file paths in the stack:
+
+`/Users/sindresorhus/dev/clean-stack/unicorn.js:2:15` → `~/dev/clean-stack/unicorn.js:2:15`
+
+
+## Related
+
+- [extrack-stack](https://github.com/sindresorhus/extract-stack) - Extract the actual stack of an error
+- [stack-utils](https://github.com/tapjs/stack-utils) - Captures and cleans stack traces
+
+
+## License
+
+MIT © [Sindre Sorhus](https://sindresorhus.com)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/LICENSE
new file mode 100644
index 000000000..19129e315
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/README.md
new file mode 100644
index 000000000..f89aa17d3
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/README.md
@@ -0,0 +1,129 @@
+# color-support
+
+A module which will endeavor to guess your terminal's level of color
+support.
+
+[](https://travis-ci.org/isaacs/color-support) [](https://coveralls.io/github/isaacs/color-support?branch=master)
+
+This is similar to `supports-color`, but it does not read
+`process.argv`.
+
+1. If not in a node environment, not supported.
+
+2. If stdout is not a TTY, not supported, unless the `ignoreTTY`
+ option is set.
+
+3. If the `TERM` environ is `dumb`, not supported, unless the
+ `ignoreDumb` option is set.
+
+4. If on Windows, then support 16 colors.
+
+5. If using Tmux, then support 256 colors.
+
+7. Handle continuous-integration servers. If `CI` or
+ `TEAMCITY_VERSION` are set in the environment, and `TRAVIS` is not
+ set, then color is not supported, unless `ignoreCI` option is set.
+
+6. Guess based on the `TERM_PROGRAM` environ. These terminals support
+ 16m colors:
+
+ - `iTerm.app` version 3.x supports 16m colors, below support 256
+ - `MacTerm` supports 16m colors
+ - `Apple_Terminal` supports 256 colors
+ - Have more things that belong on this list? Send a PR!
+
+8. Make a guess based on the `TERM` environment variable. Any
+ `xterm-256color` will get 256 colors. Any screen, xterm, vt100,
+ color, ansi, cygwin, or linux `TERM` will get 16 colors.
+
+9. If `COLORTERM` environment variable is set, then support 16 colors.
+
+10. At this point, we assume that color is not supported.
+
+## USAGE
+
+```javascript
+var testColorSupport = require('color-support')
+var colorSupport = testColorSupport(/* options object */)
+
+if (!colorSupport) {
+ console.log('color is not supported')
+} else if (colorSupport.has16m) {
+ console.log('\x1b[38;2;102;194;255m16m colors\x1b[0m')
+} else if (colorSupport.has256) {
+ console.log('\x1b[38;5;119m256 colors\x1b[0m')
+} else if (colorSupport.hasBasic) {
+ console.log('\x1b[31mbasic colors\x1b[0m')
+} else {
+ console.log('this is impossible, but colors are not supported')
+}
+```
+
+If you don't have any options to set, you can also just look at the
+flags which will all be set on the test function itself. (Of course,
+this doesn't return a falsey value when colors aren't supported, and
+doesn't allow you to set options.)
+
+```javascript
+var colorSupport = require('color-support')
+
+if (colorSupport.has16m) {
+ console.log('\x1b[38;2;102;194;255m16m colors\x1b[0m')
+} else if (colorSupport.has256) {
+ console.log('\x1b[38;5;119m256 colors\x1b[0m')
+} else if (colorSupport.hasBasic) {
+ console.log('\x1b[31mbasic colors\x1b[0m')
+} else {
+ console.log('colors are not supported')
+}
+```
+
+## Options
+
+You can pass in the following options.
+
+* ignoreTTY - default false. Ignore the `isTTY` check.
+* ignoreDumb - default false. Ignore `TERM=dumb` environ check.
+* ignoreCI - default false. Ignore `CI` environ check.
+* env - Object for environment vars. Defaults to `process.env`.
+* stream - Stream for `isTTY` check. Defaults to `process.stdout`.
+* term - String for `TERM` checking. Defaults to `env.TERM`.
+* alwaysReturn - default false. Return an object when colors aren't
+ supported (instead of returning `false`).
+* level - A number from 0 to 3. This will return a result for the
+ specified level. This is useful if you want to be able to set the
+ color support level explicitly as a number in an environment
+ variable or config, but then use the object flags in your program.
+ Except for `alwaysReturn` to return an object for level 0, all other
+ options are ignored, since no checking is done if a level is
+ explicitly set.
+
+## Return Value
+
+If no color support is available, then `false` is returned by default,
+unless the `alwaysReturn` flag is set to `true`. This is so that the
+simple question of "can I use colors or not" can treat any truthy
+return as "yes".
+
+Otherwise, the return object has the following fields:
+
+* `level` - A number from 0 to 3
+ * `0` - No color support
+ * `1` - Basic (16) color support
+ * `2` - 256 color support
+ * `3` - 16 million (true) color support
+* `hasBasic` - Boolean
+* `has256` - Boolean
+* `has16m` - Boolean
+
+## CLI
+
+You can run the `color-support` bin from the command line which will
+just dump the values as this module calculates them in whatever env
+it's run. It takes no command line arguments.
+
+## Credits
+
+This is a spiritual, if not actual, fork of
+[supports-color](http://npm.im/supports-color) by the ever prolific
+[Sindre Sorhus](http://npm.im/~sindresorhus).
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/bin.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/bin.js
new file mode 100644
index 000000000..3c0a96721
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/bin.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+var colorSupport = require('./')({alwaysReturn: true })
+console.log(JSON.stringify(colorSupport, null, 2))
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/browser.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/browser.js
new file mode 100644
index 000000000..ab5c6631a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/browser.js
@@ -0,0 +1,14 @@
+module.exports = colorSupport({ alwaysReturn: true }, colorSupport)
+
+function colorSupport(options, obj) {
+ obj = obj || {}
+ options = options || {}
+ obj.level = 0
+ obj.hasBasic = false
+ obj.has256 = false
+ obj.has16m = false
+ if (!options.alwaysReturn) {
+ return false
+ }
+ return obj
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/index.js
new file mode 100644
index 000000000..6b6f3b281
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/index.js
@@ -0,0 +1,134 @@
+// call it on itself so we can test the export val for basic stuff
+module.exports = colorSupport({ alwaysReturn: true }, colorSupport)
+
+function hasNone (obj, options) {
+ obj.level = 0
+ obj.hasBasic = false
+ obj.has256 = false
+ obj.has16m = false
+ if (!options.alwaysReturn) {
+ return false
+ }
+ return obj
+}
+
+function hasBasic (obj) {
+ obj.hasBasic = true
+ obj.has256 = false
+ obj.has16m = false
+ obj.level = 1
+ return obj
+}
+
+function has256 (obj) {
+ obj.hasBasic = true
+ obj.has256 = true
+ obj.has16m = false
+ obj.level = 2
+ return obj
+}
+
+function has16m (obj) {
+ obj.hasBasic = true
+ obj.has256 = true
+ obj.has16m = true
+ obj.level = 3
+ return obj
+}
+
+function colorSupport (options, obj) {
+ options = options || {}
+
+ obj = obj || {}
+
+ // if just requesting a specific level, then return that.
+ if (typeof options.level === 'number') {
+ switch (options.level) {
+ case 0:
+ return hasNone(obj, options)
+ case 1:
+ return hasBasic(obj)
+ case 2:
+ return has256(obj)
+ case 3:
+ return has16m(obj)
+ }
+ }
+
+ obj.level = 0
+ obj.hasBasic = false
+ obj.has256 = false
+ obj.has16m = false
+
+ if (typeof process === 'undefined' ||
+ !process ||
+ !process.stdout ||
+ !process.env ||
+ !process.platform) {
+ return hasNone(obj, options)
+ }
+
+ var env = options.env || process.env
+ var stream = options.stream || process.stdout
+ var term = options.term || env.TERM || ''
+ var platform = options.platform || process.platform
+
+ if (!options.ignoreTTY && !stream.isTTY) {
+ return hasNone(obj, options)
+ }
+
+ if (!options.ignoreDumb && term === 'dumb' && !env.COLORTERM) {
+ return hasNone(obj, options)
+ }
+
+ if (platform === 'win32') {
+ return hasBasic(obj)
+ }
+
+ if (env.TMUX) {
+ return has256(obj)
+ }
+
+ if (!options.ignoreCI && (env.CI || env.TEAMCITY_VERSION)) {
+ if (env.TRAVIS) {
+ return has256(obj)
+ } else {
+ return hasNone(obj, options)
+ }
+ }
+
+ // TODO: add more term programs
+ switch (env.TERM_PROGRAM) {
+ case 'iTerm.app':
+ var ver = env.TERM_PROGRAM_VERSION || '0.'
+ if (/^[0-2]\./.test(ver)) {
+ return has256(obj)
+ } else {
+ return has16m(obj)
+ }
+
+ case 'HyperTerm':
+ case 'Hyper':
+ return has16m(obj)
+
+ case 'MacTerm':
+ return has16m(obj)
+
+ case 'Apple_Terminal':
+ return has256(obj)
+ }
+
+ if (/^xterm-256/.test(term)) {
+ return has256(obj)
+ }
+
+ if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(term)) {
+ return hasBasic(obj)
+ }
+
+ if (env.COLORTERM) {
+ return hasBasic(obj)
+ }
+
+ return hasNone(obj, options)
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/package.json
new file mode 100644
index 000000000..f3e3b7714
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/color-support/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "color-support",
+ "version": "1.1.3",
+ "description": "A module which will endeavor to guess your terminal's level of color support.",
+ "main": "index.js",
+ "browser": "browser.js",
+ "bin": "bin.js",
+ "devDependencies": {
+ "tap": "^10.3.3"
+ },
+ "scripts": {
+ "test": "tap test/*.js --100 -J",
+ "preversion": "npm test",
+ "postversion": "npm publish",
+ "postpublish": "git push origin --all; git push origin --tags"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/isaacs/color-support.git"
+ },
+ "keywords": [
+ "terminal",
+ "color",
+ "support",
+ "xterm",
+ "truecolor",
+ "256"
+ ],
+ "author": "Isaac Z. Schlueter (http://blog.izs.me/)",
+ "license": "ISC",
+ "files": [
+ "browser.js",
+ "index.js",
+ "bin.js"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/LICENSE.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/LICENSE.md
new file mode 100644
index 000000000..6ba7a0fbb
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright © Jorge Bucaran <>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/README.md
new file mode 100644
index 000000000..693ae44d1
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/README.md
@@ -0,0 +1,134 @@
+# 🌈Colorette
+
+> Easily set your terminal text color & styles.
+
+- No dependecies
+- Automatic color support detection
+- Up to [2x faster](#benchmarks) than alternatives
+- TypeScript support
+- [`NO_COLOR`](https://no-color.org) friendly
+- Node >= `10`
+
+> [**Upgrading from Colorette `1.x`?**](https://github.com/jorgebucaran/colorette/issues/70)
+
+## Quickstart
+
+```js
+import { blue, bold, underline } from "colorette"
+
+console.log(
+ blue("I'm blue"),
+ bold(blue("da ba dee")),
+ underline(bold(blue("da ba daa")))
+)
+```
+
+Here's an example using [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals).
+
+```js
+console.log(`
+ There's a ${underline(blue("house"))},
+ With a ${bold(blue("window"))},
+ And a ${blue("corvette")}
+ And everything is blue
+`)
+```
+
+You can also nest styles without breaking existing color sequences.
+
+```js
+console.log(bold(`I'm ${blue(`da ba ${underline("dee")} da ba`)} daa`))
+```
+
+Need to override terminal color detection? You can do that too.
+
+```js
+import { createColors } from "colorette"
+
+const { blue } = createColors({ useColor: false })
+
+console.log(blue("Blue? Nope, nah"))
+```
+
+## Installation
+
+```console
+npm install colorette
+```
+
+## API
+
+### \()
+
+> See all [supported colors](#supported-colors).
+
+```js
+import { blue } from "colorette"
+
+blue("I'm blue") //=> \x1b[34mI'm blue\x1b[39m
+```
+
+### createColors()
+
+Override terminal color detection via `createColors({ useColor })`.
+
+```js
+import { createColors } from "colorette"
+
+const { blue } = createColors({ useColor: false })
+```
+
+### isColorSupported
+
+`true` if your terminal supports color, `false` otherwise. Used internally, but exposed for convenience.
+
+## Environment
+
+You can override color detection from the CLI by setting the `--no-color` or `--color` flags.
+
+```console
+$ ./example.js --no-color | ./consumer.js
+```
+
+Or if you can't use CLI flags, by setting the `NO_COLOR=` or `FORCE_COLOR=` environment variables.
+
+```console
+$ NO_COLOR= ./example.js | ./consumer.js
+```
+
+## Supported colors
+
+| Colors | Background Colors | Bright Colors | Bright Background Colors | Modifiers |
+| ------- | ----------------- | ------------- | ------------------------ | ----------------- |
+| black | bgBlack | blackBright | bgBlackBright | dim |
+| red | bgRed | redBright | bgRedBright | **bold** |
+| green | bgGreen | greenBright | bgGreenBright | hidden |
+| yellow | bgYellow | yellowBright | bgYellowBright | _italic_ |
+| blue | bgBlue | blueBright | bgBlueBright | underline |
+| magenta | bgMagenta | magentaBright | bgMagentaBright | ~~strikethrough~~ |
+| cyan | bgCyan | cyanBright | bgCyanBright | reset |
+| white | bgWhite | whiteBright | bgWhiteBright | |
+| gray | | | | |
+
+## [Benchmarks](https://github.com/jorgebucaran/colorette/actions/workflows/bench.yml)
+
+```console
+npm --prefix bench start
+```
+
+```diff
+ chalk 1,786,703 ops/sec
+ kleur 1,618,960 ops/sec
+ colors 646,823 ops/sec
+ ansi-colors 786,149 ops/sec
+ picocolors 2,871,758 ops/sec
++ colorette 3,002,751 ops/sec
+```
+
+## Acknowledgments
+
+Colorette started out in 2015 by [@jorgebucaran](https://github.com/jorgebucaran) as a lightweight alternative to [Chalk](https://github.com/chalk/chalk) and was introduced originally as [Clor](https://github.com/jorgebucaran/colorette/commit/b01b5b9961ceb7df878583a3002e836fae9e37ce). Our terminal color detection logic borrows heavily from [@sindresorhus](https://github.com/sindresorhus) and [@Qix-](https://github.com/Qix-) work on Chalk. The idea of slicing strings to clear bleeding sequences was adapted from a similar technique used by [@alexeyraspopov](https://github.com/alexeyraspopov) in [picocolors](https://github.com/alexeyraspopov/picocolors). Thank you to all our contributors! <3
+
+## License
+
+[MIT](LICENSE.md)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.cjs b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.cjs
new file mode 100644
index 000000000..baf0e6434
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.cjs
@@ -0,0 +1,218 @@
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+var tty = require('tty');
+
+function _interopNamespace(e) {
+ if (e && e.__esModule) return e;
+ var n = Object.create(null);
+ if (e) {
+ Object.keys(e).forEach(function (k) {
+ if (k !== 'default') {
+ var d = Object.getOwnPropertyDescriptor(e, k);
+ Object.defineProperty(n, k, d.get ? d : {
+ enumerable: true,
+ get: function () { return e[k]; }
+ });
+ }
+ });
+ }
+ n["default"] = e;
+ return Object.freeze(n);
+}
+
+var tty__namespace = /*#__PURE__*/_interopNamespace(tty);
+
+const {
+ env = {},
+ argv = [],
+ platform = "",
+} = typeof process === "undefined" ? {} : process;
+
+const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
+const isForced = "FORCE_COLOR" in env || argv.includes("--color");
+const isWindows = platform === "win32";
+const isDumbTerminal = env.TERM === "dumb";
+
+const isCompatibleTerminal =
+ tty__namespace && tty__namespace.isatty && tty__namespace.isatty(1) && env.TERM && !isDumbTerminal;
+
+const isCI =
+ "CI" in env &&
+ ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
+
+const isColorSupported =
+ !isDisabled &&
+ (isForced || (isWindows && !isDumbTerminal) || isCompatibleTerminal || isCI);
+
+const replaceClose = (
+ index,
+ string,
+ close,
+ replace,
+ head = string.substring(0, index) + replace,
+ tail = string.substring(index + close.length),
+ next = tail.indexOf(close)
+) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
+
+const clearBleed = (index, string, open, close, replace) =>
+ index < 0
+ ? open + string + close
+ : open + replaceClose(index, string, close, replace) + close;
+
+const filterEmpty =
+ (open, close, replace = open, at = open.length + 1) =>
+ (string) =>
+ string || !(string === "" || string === undefined)
+ ? clearBleed(
+ ("" + string).indexOf(close, at),
+ string,
+ open,
+ close,
+ replace
+ )
+ : "";
+
+const init = (open, close, replace) =>
+ filterEmpty(`\x1b[${open}m`, `\x1b[${close}m`, replace);
+
+const colors = {
+ reset: init(0, 0),
+ bold: init(1, 22, "\x1b[22m\x1b[1m"),
+ dim: init(2, 22, "\x1b[22m\x1b[2m"),
+ italic: init(3, 23),
+ underline: init(4, 24),
+ inverse: init(7, 27),
+ hidden: init(8, 28),
+ strikethrough: init(9, 29),
+ black: init(30, 39),
+ red: init(31, 39),
+ green: init(32, 39),
+ yellow: init(33, 39),
+ blue: init(34, 39),
+ magenta: init(35, 39),
+ cyan: init(36, 39),
+ white: init(37, 39),
+ gray: init(90, 39),
+ bgBlack: init(40, 49),
+ bgRed: init(41, 49),
+ bgGreen: init(42, 49),
+ bgYellow: init(43, 49),
+ bgBlue: init(44, 49),
+ bgMagenta: init(45, 49),
+ bgCyan: init(46, 49),
+ bgWhite: init(47, 49),
+ blackBright: init(90, 39),
+ redBright: init(91, 39),
+ greenBright: init(92, 39),
+ yellowBright: init(93, 39),
+ blueBright: init(94, 39),
+ magentaBright: init(95, 39),
+ cyanBright: init(96, 39),
+ whiteBright: init(97, 39),
+ bgBlackBright: init(100, 49),
+ bgRedBright: init(101, 49),
+ bgGreenBright: init(102, 49),
+ bgYellowBright: init(103, 49),
+ bgBlueBright: init(104, 49),
+ bgMagentaBright: init(105, 49),
+ bgCyanBright: init(106, 49),
+ bgWhiteBright: init(107, 49),
+};
+
+const createColors = ({ useColor = isColorSupported } = {}) =>
+ useColor
+ ? colors
+ : Object.keys(colors).reduce(
+ (colors, key) => ({ ...colors, [key]: String }),
+ {}
+ );
+
+const {
+ reset,
+ bold,
+ dim,
+ italic,
+ underline,
+ inverse,
+ hidden,
+ strikethrough,
+ black,
+ red,
+ green,
+ yellow,
+ blue,
+ magenta,
+ cyan,
+ white,
+ gray,
+ bgBlack,
+ bgRed,
+ bgGreen,
+ bgYellow,
+ bgBlue,
+ bgMagenta,
+ bgCyan,
+ bgWhite,
+ blackBright,
+ redBright,
+ greenBright,
+ yellowBright,
+ blueBright,
+ magentaBright,
+ cyanBright,
+ whiteBright,
+ bgBlackBright,
+ bgRedBright,
+ bgGreenBright,
+ bgYellowBright,
+ bgBlueBright,
+ bgMagentaBright,
+ bgCyanBright,
+ bgWhiteBright,
+} = createColors();
+
+exports.bgBlack = bgBlack;
+exports.bgBlackBright = bgBlackBright;
+exports.bgBlue = bgBlue;
+exports.bgBlueBright = bgBlueBright;
+exports.bgCyan = bgCyan;
+exports.bgCyanBright = bgCyanBright;
+exports.bgGreen = bgGreen;
+exports.bgGreenBright = bgGreenBright;
+exports.bgMagenta = bgMagenta;
+exports.bgMagentaBright = bgMagentaBright;
+exports.bgRed = bgRed;
+exports.bgRedBright = bgRedBright;
+exports.bgWhite = bgWhite;
+exports.bgWhiteBright = bgWhiteBright;
+exports.bgYellow = bgYellow;
+exports.bgYellowBright = bgYellowBright;
+exports.black = black;
+exports.blackBright = blackBright;
+exports.blue = blue;
+exports.blueBright = blueBright;
+exports.bold = bold;
+exports.createColors = createColors;
+exports.cyan = cyan;
+exports.cyanBright = cyanBright;
+exports.dim = dim;
+exports.gray = gray;
+exports.green = green;
+exports.greenBright = greenBright;
+exports.hidden = hidden;
+exports.inverse = inverse;
+exports.isColorSupported = isColorSupported;
+exports.italic = italic;
+exports.magenta = magenta;
+exports.magentaBright = magentaBright;
+exports.red = red;
+exports.redBright = redBright;
+exports.reset = reset;
+exports.strikethrough = strikethrough;
+exports.underline = underline;
+exports.white = white;
+exports.whiteBright = whiteBright;
+exports.yellow = yellow;
+exports.yellowBright = yellowBright;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.d.ts
new file mode 100644
index 000000000..ba32c9f34
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.d.ts
@@ -0,0 +1,93 @@
+declare module "colorette" {
+ type Color = (text: string | number) => string
+
+ interface Colorette {
+ reset: Color
+ bold: Color
+ dim: Color
+ italic: Color
+ underline: Color
+ inverse: Color
+ hidden: Color
+ strikethrough: Color
+ black: Color
+ red: Color
+ green: Color
+ yellow: Color
+ blue: Color
+ magenta: Color
+ cyan: Color
+ white: Color
+ gray: Color
+ bgBlack: Color
+ bgRed: Color
+ bgGreen: Color
+ bgYellow: Color
+ bgBlue: Color
+ bgMagenta: Color
+ bgCyan: Color
+ bgWhite: Color
+ blackBright: Color
+ redBright: Color
+ greenBright: Color
+ yellowBright: Color
+ blueBright: Color
+ magentaBright: Color
+ cyanBright: Color
+ whiteBright: Color
+ bgBlackBright: Color
+ bgRedBright: Color
+ bgGreenBright: Color
+ bgYellowBright: Color
+ bgBlueBright: Color
+ bgMagentaBright: Color
+ bgCyanBright: Color
+ bgWhiteBright: Color
+ }
+
+ const reset: Color
+ const bold: Color
+ const dim: Color
+ const italic: Color
+ const underline: Color
+ const inverse: Color
+ const hidden: Color
+ const strikethrough: Color
+ const black: Color
+ const red: Color
+ const green: Color
+ const yellow: Color
+ const blue: Color
+ const magenta: Color
+ const cyan: Color
+ const white: Color
+ const gray: Color
+ const bgBlack: Color
+ const bgRed: Color
+ const bgGreen: Color
+ const bgYellow: Color
+ const bgBlue: Color
+ const bgMagenta: Color
+ const bgCyan: Color
+ const bgWhite: Color
+ const blackBright: Color
+ const redBright: Color
+ const greenBright: Color
+ const yellowBright: Color
+ const blueBright: Color
+ const magentaBright: Color
+ const cyanBright: Color
+ const whiteBright: Color
+ const bgBlackBright: Color
+ const bgRedBright: Color
+ const bgGreenBright: Color
+ const bgYellowBright: Color
+ const bgBlueBright: Color
+ const bgMagentaBright: Color
+ const bgCyanBright: Color
+ const bgWhiteBright: Color
+
+ const isColorSupported: boolean
+
+ function createColors(options?: { useColor: boolean }): Colorette
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.js
new file mode 100644
index 000000000..0d64e6b3c
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/index.js
@@ -0,0 +1,150 @@
+import * as tty from "tty"
+
+const {
+ env = {},
+ argv = [],
+ platform = "",
+} = typeof process === "undefined" ? {} : process
+
+const isDisabled = "NO_COLOR" in env || argv.includes("--no-color")
+const isForced = "FORCE_COLOR" in env || argv.includes("--color")
+const isWindows = platform === "win32"
+const isDumbTerminal = env.TERM === "dumb"
+
+const isCompatibleTerminal =
+ tty && tty.isatty && tty.isatty(1) && env.TERM && !isDumbTerminal
+
+const isCI =
+ "CI" in env &&
+ ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env)
+
+export const isColorSupported =
+ !isDisabled &&
+ (isForced || (isWindows && !isDumbTerminal) || isCompatibleTerminal || isCI)
+
+const replaceClose = (
+ index,
+ string,
+ close,
+ replace,
+ head = string.substring(0, index) + replace,
+ tail = string.substring(index + close.length),
+ next = tail.indexOf(close)
+) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace))
+
+const clearBleed = (index, string, open, close, replace) =>
+ index < 0
+ ? open + string + close
+ : open + replaceClose(index, string, close, replace) + close
+
+const filterEmpty =
+ (open, close, replace = open, at = open.length + 1) =>
+ (string) =>
+ string || !(string === "" || string === undefined)
+ ? clearBleed(
+ ("" + string).indexOf(close, at),
+ string,
+ open,
+ close,
+ replace
+ )
+ : ""
+
+const init = (open, close, replace) =>
+ filterEmpty(`\x1b[${open}m`, `\x1b[${close}m`, replace)
+
+const colors = {
+ reset: init(0, 0),
+ bold: init(1, 22, "\x1b[22m\x1b[1m"),
+ dim: init(2, 22, "\x1b[22m\x1b[2m"),
+ italic: init(3, 23),
+ underline: init(4, 24),
+ inverse: init(7, 27),
+ hidden: init(8, 28),
+ strikethrough: init(9, 29),
+ black: init(30, 39),
+ red: init(31, 39),
+ green: init(32, 39),
+ yellow: init(33, 39),
+ blue: init(34, 39),
+ magenta: init(35, 39),
+ cyan: init(36, 39),
+ white: init(37, 39),
+ gray: init(90, 39),
+ bgBlack: init(40, 49),
+ bgRed: init(41, 49),
+ bgGreen: init(42, 49),
+ bgYellow: init(43, 49),
+ bgBlue: init(44, 49),
+ bgMagenta: init(45, 49),
+ bgCyan: init(46, 49),
+ bgWhite: init(47, 49),
+ blackBright: init(90, 39),
+ redBright: init(91, 39),
+ greenBright: init(92, 39),
+ yellowBright: init(93, 39),
+ blueBright: init(94, 39),
+ magentaBright: init(95, 39),
+ cyanBright: init(96, 39),
+ whiteBright: init(97, 39),
+ bgBlackBright: init(100, 49),
+ bgRedBright: init(101, 49),
+ bgGreenBright: init(102, 49),
+ bgYellowBright: init(103, 49),
+ bgBlueBright: init(104, 49),
+ bgMagentaBright: init(105, 49),
+ bgCyanBright: init(106, 49),
+ bgWhiteBright: init(107, 49),
+}
+
+export const createColors = ({ useColor = isColorSupported } = {}) =>
+ useColor
+ ? colors
+ : Object.keys(colors).reduce(
+ (colors, key) => ({ ...colors, [key]: String }),
+ {}
+ )
+
+export const {
+ reset,
+ bold,
+ dim,
+ italic,
+ underline,
+ inverse,
+ hidden,
+ strikethrough,
+ black,
+ red,
+ green,
+ yellow,
+ blue,
+ magenta,
+ cyan,
+ white,
+ gray,
+ bgBlack,
+ bgRed,
+ bgGreen,
+ bgYellow,
+ bgBlue,
+ bgMagenta,
+ bgCyan,
+ bgWhite,
+ blackBright,
+ redBright,
+ greenBright,
+ yellowBright,
+ blueBright,
+ magentaBright,
+ cyanBright,
+ whiteBright,
+ bgBlackBright,
+ bgRedBright,
+ bgGreenBright,
+ bgYellowBright,
+ bgBlueBright,
+ bgMagentaBright,
+ bgCyanBright,
+ bgWhiteBright,
+} = createColors()
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/package.json
new file mode 100644
index 000000000..ab90f9d4e
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/colorette/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "colorette",
+ "version": "2.0.19",
+ "type": "module",
+ "main": "index.cjs",
+ "module": "index.js",
+ "types": "index.d.ts",
+ "description": "🌈Easily set your terminal text color & styles.",
+ "repository": "jorgebucaran/colorette",
+ "license": "MIT",
+ "exports": {
+ "./package.json": "./package.json",
+ ".": {
+ "require": "./index.cjs",
+ "import": "./index.js"
+ }
+ },
+ "files": [
+ "*.*(c)[tj]s*"
+ ],
+ "author": "Jorge Bucaran",
+ "keywords": [
+ "terminal",
+ "styles",
+ "color",
+ "ansi"
+ ],
+ "scripts": {
+ "test": "c8 twist tests/*.js",
+ "build": "npx rollup --format cjs --input index.js --file index.cjs",
+ "deploy": "npm test && git commit --all --message $tag && git tag --sign $tag --message $tag && git push && git push --tags",
+ "release": "tag=$npm_package_version npm run deploy && npm publish --access public",
+ "prepare": "npm run build"
+ },
+ "devDependencies": {
+ "c8": "*",
+ "twist": "*"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/commander/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/commander/LICENSE
new file mode 100644
index 000000000..10f997ab1
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/commander/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2011 TJ Holowaychuk
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/commander/Readme.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/commander/Readme.md
new file mode 100644
index 000000000..095e2aefb
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/commander/Readme.md
@@ -0,0 +1,1134 @@
+# Commander.js
+
+[](https://github.com/tj/commander.js/actions?query=workflow%3A%22build%22)
+[](https://www.npmjs.org/package/commander)
+[](https://npmcharts.com/compare/commander?minimal=true)
+[](https://packagephobia.now.sh/result?p=commander)
+
+The complete solution for [node.js](http://nodejs.org) command-line interfaces.
+
+Read this in other languages: English | [简体中文](./Readme_zh-CN.md)
+
+- [Commander.js](#commanderjs)
+ - [Installation](#installation)
+ - [Quick Start](#quick-start)
+ - [Declaring _program_ variable](#declaring-program-variable)
+ - [Options](#options)
+ - [Common option types, boolean and value](#common-option-types-boolean-and-value)
+ - [Default option value](#default-option-value)
+ - [Other option types, negatable boolean and boolean|value](#other-option-types-negatable-boolean-and-booleanvalue)
+ - [Required option](#required-option)
+ - [Variadic option](#variadic-option)
+ - [Version option](#version-option)
+ - [More configuration](#more-configuration)
+ - [Custom option processing](#custom-option-processing)
+ - [Commands](#commands)
+ - [Command-arguments](#command-arguments)
+ - [More configuration](#more-configuration-1)
+ - [Custom argument processing](#custom-argument-processing)
+ - [Action handler](#action-handler)
+ - [Stand-alone executable (sub)commands](#stand-alone-executable-subcommands)
+ - [Life cycle hooks](#life-cycle-hooks)
+ - [Automated help](#automated-help)
+ - [Custom help](#custom-help)
+ - [Display help after errors](#display-help-after-errors)
+ - [Display help from code](#display-help-from-code)
+ - [.name](#name)
+ - [.usage](#usage)
+ - [.description and .summary](#description-and-summary)
+ - [.helpOption(flags, description)](#helpoptionflags-description)
+ - [.addHelpCommand()](#addhelpcommand)
+ - [More configuration](#more-configuration-2)
+ - [Custom event listeners](#custom-event-listeners)
+ - [Bits and pieces](#bits-and-pieces)
+ - [.parse() and .parseAsync()](#parse-and-parseasync)
+ - [Parsing Configuration](#parsing-configuration)
+ - [Legacy options as properties](#legacy-options-as-properties)
+ - [TypeScript](#typescript)
+ - [createCommand()](#createcommand)
+ - [Node options such as `--harmony`](#node-options-such-as---harmony)
+ - [Debugging stand-alone executable subcommands](#debugging-stand-alone-executable-subcommands)
+ - [Display error](#display-error)
+ - [Override exit and output handling](#override-exit-and-output-handling)
+ - [Additional documentation](#additional-documentation)
+ - [Support](#support)
+ - [Commander for enterprise](#commander-for-enterprise)
+
+For information about terms used in this document see: [terminology](./docs/terminology.md)
+
+## Installation
+
+```sh
+npm install commander
+```
+
+## Quick Start
+
+You write code to describe your command line interface.
+Commander looks after parsing the arguments into options and command-arguments,
+displays usage errors for problems, and implements a help system.
+
+Commander is strict and displays an error for unrecognised options.
+The two most used option types are a boolean option, and an option which takes its value from the following argument.
+
+Example file: [split.js](./examples/split.js)
+
+```js
+const { program } = require('commander');
+
+program
+ .option('--first')
+ .option('-s, --separator ');
+
+program.parse();
+
+const options = program.opts();
+const limit = options.first ? 1 : undefined;
+console.log(program.args[0].split(options.separator, limit));
+```
+
+```console
+$ node split.js -s / --fits a/b/c
+error: unknown option '--fits'
+(Did you mean --first?)
+$ node split.js -s / --first a/b/c
+[ 'a' ]
+```
+
+Here is a more complete program using a subcommand and with descriptions for the help. In a multi-command program, you have an action handler for each command (or stand-alone executables for the commands).
+
+Example file: [string-util.js](./examples/string-util.js)
+
+```js
+const { Command } = require('commander');
+const program = new Command();
+
+program
+ .name('string-util')
+ .description('CLI to some JavaScript string utilities')
+ .version('0.8.0');
+
+program.command('split')
+ .description('Split a string into substrings and display as an array')
+ .argument('', 'string to split')
+ .option('--first', 'display just the first substring')
+ .option('-s, --separator ', 'separator character', ',')
+ .action((str, options) => {
+ const limit = options.first ? 1 : undefined;
+ console.log(str.split(options.separator, limit));
+ });
+
+program.parse();
+```
+
+```console
+$ node string-util.js help split
+Usage: string-util split [options]
+
+Split a string into substrings and display as an array.
+
+Arguments:
+ string string to split
+
+Options:
+ --first display just the first substring
+ -s, --separator separator character (default: ",")
+ -h, --help display help for command
+
+$ node string-util.js split --separator=/ a/b/c
+[ 'a', 'b', 'c' ]
+```
+
+More samples can be found in the [examples](https://github.com/tj/commander.js/tree/master/examples) directory.
+
+## Declaring _program_ variable
+
+Commander exports a global object which is convenient for quick programs.
+This is used in the examples in this README for brevity.
+
+```js
+// CommonJS (.cjs)
+const { program } = require('commander');
+```
+
+For larger programs which may use commander in multiple ways, including unit testing, it is better to create a local Command object to use.
+
+```js
+// CommonJS (.cjs)
+const { Command } = require('commander');
+const program = new Command();
+```
+
+```js
+// ECMAScript (.mjs)
+import { Command } from 'commander';
+const program = new Command();
+```
+
+```ts
+// TypeScript (.ts)
+import { Command } from 'commander';
+const program = new Command();
+```
+
+## Options
+
+Options are defined with the `.option()` method, also serving as documentation for the options. Each option can have a short flag (single character) and a long name, separated by a comma or space or vertical bar ('|').
+
+The parsed options can be accessed by calling `.opts()` on a `Command` object, and are passed to the action handler.
+
+Multi-word options such as "--template-engine" are camel-cased, becoming `program.opts().templateEngine` etc.
+
+An option and its option-argument can be separated by a space, or combined into the same argument. The option-argument can follow the short option directly or follow an `=` for a long option.
+
+```sh
+serve -p 80
+serve -p80
+serve --port 80
+serve --port=80
+```
+
+You can use `--` to indicate the end of the options, and any remaining arguments will be used without being interpreted.
+
+By default options on the command line are not positional, and can be specified before or after other arguments.
+
+There are additional related routines for when `.opts()` is not enough:
+
+- `.optsWithGlobals()` returns merged local and global option values
+- `.getOptionValue()` and `.setOptionValue()` work with a single option value
+- `.getOptionValueSource()` and `.setOptionValueWithSource()` include where the option value came from
+
+### Common option types, boolean and value
+
+The two most used option types are a boolean option, and an option which takes its value
+from the following argument (declared with angle brackets like `--expect `). Both are `undefined` unless specified on command line.
+
+Example file: [options-common.js](./examples/options-common.js)
+
+```js
+program
+ .option('-d, --debug', 'output extra debugging')
+ .option('-s, --small', 'small pizza size')
+ .option('-p, --pizza-type ', 'flavour of pizza');
+
+program.parse(process.argv);
+
+const options = program.opts();
+if (options.debug) console.log(options);
+console.log('pizza details:');
+if (options.small) console.log('- small pizza size');
+if (options.pizzaType) console.log(`- ${options.pizzaType}`);
+```
+
+```console
+$ pizza-options -p
+error: option '-p, --pizza-type ' argument missing
+$ pizza-options -d -s -p vegetarian
+{ debug: true, small: true, pizzaType: 'vegetarian' }
+pizza details:
+- small pizza size
+- vegetarian
+$ pizza-options --pizza-type=cheese
+pizza details:
+- cheese
+```
+
+Multiple boolean short options may be combined together following the dash, and may be followed by a single short option taking a value.
+For example `-d -s -p cheese` may be written as `-ds -p cheese` or even `-dsp cheese`.
+
+Options with an expected option-argument are greedy and will consume the following argument whatever the value.
+So `--id -xyz` reads `-xyz` as the option-argument.
+
+`program.parse(arguments)` processes the arguments, leaving any args not consumed by the program options in the `program.args` array. The parameter is optional and defaults to `process.argv`.
+
+### Default option value
+
+You can specify a default value for an option.
+
+Example file: [options-defaults.js](./examples/options-defaults.js)
+
+```js
+program
+ .option('-c, --cheese ', 'add the specified type of cheese', 'blue');
+
+program.parse();
+
+console.log(`cheese: ${program.opts().cheese}`);
+```
+
+```console
+$ pizza-options
+cheese: blue
+$ pizza-options --cheese stilton
+cheese: stilton
+```
+
+### Other option types, negatable boolean and boolean|value
+
+You can define a boolean option long name with a leading `no-` to set the option value to false when used.
+Defined alone this also makes the option true by default.
+
+If you define `--foo` first, adding `--no-foo` does not change the default value from what it would
+otherwise be.
+
+Example file: [options-negatable.js](./examples/options-negatable.js)
+
+```js
+program
+ .option('--no-sauce', 'Remove sauce')
+ .option('--cheese ', 'cheese flavour', 'mozzarella')
+ .option('--no-cheese', 'plain with no cheese')
+ .parse();
+
+const options = program.opts();
+const sauceStr = options.sauce ? 'sauce' : 'no sauce';
+const cheeseStr = (options.cheese === false) ? 'no cheese' : `${options.cheese} cheese`;
+console.log(`You ordered a pizza with ${sauceStr} and ${cheeseStr}`);
+```
+
+```console
+$ pizza-options
+You ordered a pizza with sauce and mozzarella cheese
+$ pizza-options --sauce
+error: unknown option '--sauce'
+$ pizza-options --cheese=blue
+You ordered a pizza with sauce and blue cheese
+$ pizza-options --no-sauce --no-cheese
+You ordered a pizza with no sauce and no cheese
+```
+
+You can specify an option which may be used as a boolean option but may optionally take an option-argument
+(declared with square brackets like `--optional [value]`).
+
+Example file: [options-boolean-or-value.js](./examples/options-boolean-or-value.js)
+
+```js
+program
+ .option('-c, --cheese [type]', 'Add cheese with optional type');
+
+program.parse(process.argv);
+
+const options = program.opts();
+if (options.cheese === undefined) console.log('no cheese');
+else if (options.cheese === true) console.log('add cheese');
+else console.log(`add cheese type ${options.cheese}`);
+```
+
+```console
+$ pizza-options
+no cheese
+$ pizza-options --cheese
+add cheese
+$ pizza-options --cheese mozzarella
+add cheese type mozzarella
+```
+
+Options with an optional option-argument are not greedy and will ignore arguments starting with a dash.
+So `id` behaves as a boolean option for `--id -5`, but you can use a combined form if needed like `--id=-5`.
+
+For information about possible ambiguous cases, see [options taking varying arguments](./docs/options-taking-varying-arguments.md).
+
+### Required option
+
+You may specify a required (mandatory) option using `.requiredOption()`. The option must have a value after parsing, usually specified on the command line, or perhaps from a default value (say from environment). The method is otherwise the same as `.option()` in format, taking flags and description, and optional default value or custom processing.
+
+Example file: [options-required.js](./examples/options-required.js)
+
+```js
+program
+ .requiredOption('-c, --cheese ', 'pizza must have cheese');
+
+program.parse();
+```
+
+```console
+$ pizza
+error: required option '-c, --cheese ' not specified
+```
+
+### Variadic option
+
+You may make an option variadic by appending `...` to the value placeholder when declaring the option. On the command line you
+can then specify multiple option-arguments, and the parsed option value will be an array. The extra arguments
+are read until the first argument starting with a dash. The special argument `--` stops option processing entirely. If a value
+is specified in the same argument as the option then no further values are read.
+
+Example file: [options-variadic.js](./examples/options-variadic.js)
+
+```js
+program
+ .option('-n, --number ', 'specify numbers')
+ .option('-l, --letter [letters...]', 'specify letters');
+
+program.parse();
+
+console.log('Options: ', program.opts());
+console.log('Remaining arguments: ', program.args);
+```
+
+```console
+$ collect -n 1 2 3 --letter a b c
+Options: { number: [ '1', '2', '3' ], letter: [ 'a', 'b', 'c' ] }
+Remaining arguments: []
+$ collect --letter=A -n80 operand
+Options: { number: [ '80' ], letter: [ 'A' ] }
+Remaining arguments: [ 'operand' ]
+$ collect --letter -n 1 -n 2 3 -- operand
+Options: { number: [ '1', '2', '3' ], letter: true }
+Remaining arguments: [ 'operand' ]
+```
+
+For information about possible ambiguous cases, see [options taking varying arguments](./docs/options-taking-varying-arguments.md).
+
+### Version option
+
+The optional `version` method adds handling for displaying the command version. The default option flags are `-V` and `--version`, and when present the command prints the version number and exits.
+
+```js
+program.version('0.0.1');
+```
+
+```console
+$ ./examples/pizza -V
+0.0.1
+```
+
+You may change the flags and description by passing additional parameters to the `version` method, using
+the same syntax for flags as the `option` method.
+
+```js
+program.version('0.0.1', '-v, --vers', 'output the current version');
+```
+
+### More configuration
+
+You can add most options using the `.option()` method, but there are some additional features available
+by constructing an `Option` explicitly for less common cases.
+
+Example files: [options-extra.js](./examples/options-extra.js), [options-env.js](./examples/options-env.js), [options-conflicts.js](./examples/options-conflicts.js), [options-implies.js](./examples/options-implies.js)
+
+```js
+program
+ .addOption(new Option('-s, --secret').hideHelp())
+ .addOption(new Option('-t, --timeout ', 'timeout in seconds').default(60, 'one minute'))
+ .addOption(new Option('-d, --drink ', 'drink size').choices(['small', 'medium', 'large']))
+ .addOption(new Option('-p, --port ', 'port number').env('PORT'))
+ .addOption(new Option('--donate [amount]', 'optional donation in dollars').preset('20').argParser(parseFloat))
+ .addOption(new Option('--disable-server', 'disables the server').conflicts('port'))
+ .addOption(new Option('--free-drink', 'small drink included free ').implies({ drink: 'small' }));
+```
+
+```console
+$ extra --help
+Usage: help [options]
+
+Options:
+ -t, --timeout timeout in seconds (default: one minute)
+ -d, --drink drink cup size (choices: "small", "medium", "large")
+ -p, --port port number (env: PORT)
+ --donate [amount] optional donation in dollars (preset: "20")
+ --disable-server disables the server
+ --free-drink small drink included free
+ -h, --help display help for command
+
+$ extra --drink huge
+error: option '-d, --drink ' argument 'huge' is invalid. Allowed choices are small, medium, large.
+
+$ PORT=80 extra --donate --free-drink
+Options: { timeout: 60, donate: 20, port: '80', freeDrink: true, drink: 'small' }
+
+$ extra --disable-server --port 8000
+error: option '--disable-server' cannot be used with option '-p, --port '
+```
+
+Specify a required (mandatory) option using the `Option` method `.makeOptionMandatory()`. This matches the `Command` method [.requiredOption()](#required-option).
+
+### Custom option processing
+
+You may specify a function to do custom processing of option-arguments. The callback function receives two parameters,
+the user specified option-argument and the previous value for the option. It returns the new value for the option.
+
+This allows you to coerce the option-argument to the desired type, or accumulate values, or do entirely custom processing.
+
+You can optionally specify the default/starting value for the option after the function parameter.
+
+Example file: [options-custom-processing.js](./examples/options-custom-processing.js)
+
+```js
+function myParseInt(value, dummyPrevious) {
+ // parseInt takes a string and a radix
+ const parsedValue = parseInt(value, 10);
+ if (isNaN(parsedValue)) {
+ throw new commander.InvalidArgumentError('Not a number.');
+ }
+ return parsedValue;
+}
+
+function increaseVerbosity(dummyValue, previous) {
+ return previous + 1;
+}
+
+function collect(value, previous) {
+ return previous.concat([value]);
+}
+
+function commaSeparatedList(value, dummyPrevious) {
+ return value.split(',');
+}
+
+program
+ .option('-f, --float ', 'float argument', parseFloat)
+ .option('-i, --integer ', 'integer argument', myParseInt)
+ .option('-v, --verbose', 'verbosity that can be increased', increaseVerbosity, 0)
+ .option('-c, --collect ', 'repeatable value', collect, [])
+ .option('-l, --list ', 'comma separated list', commaSeparatedList)
+;
+
+program.parse();
+
+const options = program.opts();
+if (options.float !== undefined) console.log(`float: ${options.float}`);
+if (options.integer !== undefined) console.log(`integer: ${options.integer}`);
+if (options.verbose > 0) console.log(`verbosity: ${options.verbose}`);
+if (options.collect.length > 0) console.log(options.collect);
+if (options.list !== undefined) console.log(options.list);
+```
+
+```console
+$ custom -f 1e2
+float: 100
+$ custom --integer 2
+integer: 2
+$ custom -v -v -v
+verbose: 3
+$ custom -c a -c b -c c
+[ 'a', 'b', 'c' ]
+$ custom --list x,y,z
+[ 'x', 'y', 'z' ]
+```
+
+## Commands
+
+You can specify (sub)commands using `.command()` or `.addCommand()`. There are two ways these can be implemented: using an action handler attached to the command, or as a stand-alone executable file (described in more detail later). The subcommands may be nested ([example](./examples/nestedCommands.js)).
+
+In the first parameter to `.command()` you specify the command name. You may append the command-arguments after the command name, or specify them separately using `.argument()`. The arguments may be `` or `[optional]`, and the last argument may also be `variadic...`.
+
+You can use `.addCommand()` to add an already configured subcommand to the program.
+
+For example:
+
+```js
+// Command implemented using action handler (description is supplied separately to `.command`)
+// Returns new command for configuring.
+program
+ .command('clone [destination]')
+ .description('clone a repository into a newly created directory')
+ .action((source, destination) => {
+ console.log('clone command called');
+ });
+
+// Command implemented using stand-alone executable file, indicated by adding description as second parameter to `.command`.
+// Returns `this` for adding more commands.
+program
+ .command('start ', 'start named service')
+ .command('stop [service]', 'stop named service, or all if no name supplied');
+
+// Command prepared separately.
+// Returns `this` for adding more commands.
+program
+ .addCommand(build.makeBuildCommand());
+```
+
+Configuration options can be passed with the call to `.command()` and `.addCommand()`. Specifying `hidden: true` will
+remove the command from the generated help output. Specifying `isDefault: true` will run the subcommand if no other
+subcommand is specified ([example](./examples/defaultCommand.js)).
+
+You can add alternative names for a command with `.alias()`. ([example](./examples/alias.js))
+
+For safety, `.addCommand()` does not automatically copy the inherited settings from the parent command. There is a helper routine `.copyInheritedSettings()` for copying the settings when they are wanted.
+
+### Command-arguments
+
+For subcommands, you can specify the argument syntax in the call to `.command()` (as shown above). This
+is the only method usable for subcommands implemented using a stand-alone executable, but for other subcommands
+you can instead use the following method.
+
+To configure a command, you can use `.argument()` to specify each expected command-argument.
+You supply the argument name and an optional description. The argument may be `` or `[optional]`.
+You can specify a default value for an optional command-argument.
+
+Example file: [argument.js](./examples/argument.js)
+
+```js
+program
+ .version('0.1.0')
+ .argument('', 'user to login')
+ .argument('[password]', 'password for user, if required', 'no password given')
+ .action((username, password) => {
+ console.log('username:', username);
+ console.log('password:', password);
+ });
+```
+
+ The last argument of a command can be variadic, and only the last argument. To make an argument variadic you
+ append `...` to the argument name. A variadic argument is passed to the action handler as an array. For example:
+
+```js
+program
+ .version('0.1.0')
+ .command('rmdir')
+ .argument('')
+ .action(function (dirs) {
+ dirs.forEach((dir) => {
+ console.log('rmdir %s', dir);
+ });
+ });
+```
+
+There is a convenience method to add multiple arguments at once, but without descriptions:
+
+```js
+program
+ .arguments(' ');
+```
+
+#### More configuration
+
+There are some additional features available by constructing an `Argument` explicitly for less common cases.
+
+Example file: [arguments-extra.js](./examples/arguments-extra.js)
+
+```js
+program
+ .addArgument(new commander.Argument('', 'drink cup size').choices(['small', 'medium', 'large']))
+ .addArgument(new commander.Argument('[timeout]', 'timeout in seconds').default(60, 'one minute'))
+```
+
+#### Custom argument processing
+
+You may specify a function to do custom processing of command-arguments (like for option-arguments).
+The callback function receives two parameters, the user specified command-argument and the previous value for the argument.
+It returns the new value for the argument.
+
+The processed argument values are passed to the action handler, and saved as `.processedArgs`.
+
+You can optionally specify the default/starting value for the argument after the function parameter.
+
+Example file: [arguments-custom-processing.js](./examples/arguments-custom-processing.js)
+
+```js
+program
+ .command('add')
+ .argument('', 'integer argument', myParseInt)
+ .argument('[second]', 'integer argument', myParseInt, 1000)
+ .action((first, second) => {
+ console.log(`${first} + ${second} = ${first + second}`);
+ })
+;
+```
+
+### Action handler
+
+The action handler gets passed a parameter for each command-argument you declared, and two additional parameters
+which are the parsed options and the command object itself.
+
+Example file: [thank.js](./examples/thank.js)
+
+```js
+program
+ .argument('')
+ .option('-t, --title ', 'title to use before name')
+ .option('-d, --debug', 'display some debugging')
+ .action((name, options, command) => {
+ if (options.debug) {
+ console.error('Called %s with options %o', command.name(), options);
+ }
+ const title = options.title ? `${options.title} ` : '';
+ console.log(`Thank-you ${title}${name}`);
+ });
+```
+
+If you prefer, you can work with the command directly and skip declaring the parameters for the action handler. The `this` keyword is set to the running command and can be used from a function expression (but not from an arrow function).
+
+Example file: [action-this.js](./examples/action-this.js)
+
+```js
+program
+ .command('serve')
+ .argument('
+
+```
+
+---
+
+To use iMurmurHash in Node.js, install the module using NPM:
+
+```bash
+npm install imurmurhash
+```
+
+Then simply include it in your scripts:
+
+```javascript
+MurmurHash3 = require('imurmurhash');
+```
+
+Quick Example
+-------------
+
+```javascript
+// Create the initial hash
+var hashState = MurmurHash3('string');
+
+// Incrementally add text
+hashState.hash('more strings');
+hashState.hash('even more strings');
+
+// All calls can be chained if desired
+hashState.hash('and').hash('some').hash('more');
+
+// Get a result
+hashState.result();
+// returns 0xe4ccfe6b
+```
+
+Functions
+---------
+
+### MurmurHash3 ([string], [seed])
+Get a hash state object, optionally initialized with the given _string_ and _seed_. _Seed_ must be a positive integer if provided. Calling this function without the `new` keyword will return a cached state object that has been reset. This is safe to use as long as the object is only used from a single thread and no other hashes are created while operating on this one. If this constraint cannot be met, you can use `new` to create a new state object. For example:
+
+```javascript
+// Use the cached object, calling the function again will return the same
+// object (but reset, so the current state would be lost)
+hashState = MurmurHash3();
+...
+
+// Create a new object that can be safely used however you wish. Calling the
+// function again will simply return a new state object, and no state loss
+// will occur, at the cost of creating more objects.
+hashState = new MurmurHash3();
+```
+
+Both methods can be mixed however you like if you have different use cases.
+
+---
+
+### MurmurHash3.prototype.hash (string)
+Incrementally add _string_ to the hash. This can be called as many times as you want for the hash state object, including after a call to `result()`. Returns `this` so calls can be chained.
+
+---
+
+### MurmurHash3.prototype.result ()
+Get the result of the hash as a 32-bit positive integer. This performs the tail and finalizer portions of the algorithm, but does not store the result in the state object. This means that it is perfectly safe to get results and then continue adding strings via `hash`.
+
+```javascript
+// Do the whole string at once
+MurmurHash3('this is a test string').result();
+// 0x70529328
+
+// Do part of the string, get a result, then the other part
+var m = MurmurHash3('this is a');
+m.result();
+// 0xbfc4f834
+m.hash(' test string').result();
+// 0x70529328 (same as above)
+```
+
+---
+
+### MurmurHash3.prototype.reset ([seed])
+Reset the state object for reuse, optionally using the given _seed_ (defaults to 0 like the constructor). Returns `this` so calls can be chained.
+
+---
+
+License (MIT)
+-------------
+Copyright (c) 2013 Gary Court, Jens Taylor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/imurmurhash.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/imurmurhash.js
new file mode 100644
index 000000000..e63146a2b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/imurmurhash.js
@@ -0,0 +1,138 @@
+/**
+ * @preserve
+ * JS Implementation of incremental MurmurHash3 (r150) (as of May 10, 2013)
+ *
+ * @author Jens Taylor
+ * @see http://github.com/homebrewing/brauhaus-diff
+ * @author Gary Court
+ * @see http://github.com/garycourt/murmurhash-js
+ * @author Austin Appleby
+ * @see http://sites.google.com/site/murmurhash/
+ */
+(function(){
+ var cache;
+
+ // Call this function without `new` to use the cached object (good for
+ // single-threaded environments), or with `new` to create a new object.
+ //
+ // @param {string} key A UTF-16 or ASCII string
+ // @param {number} seed An optional positive integer
+ // @return {object} A MurmurHash3 object for incremental hashing
+ function MurmurHash3(key, seed) {
+ var m = this instanceof MurmurHash3 ? this : cache;
+ m.reset(seed)
+ if (typeof key === 'string' && key.length > 0) {
+ m.hash(key);
+ }
+
+ if (m !== this) {
+ return m;
+ }
+ };
+
+ // Incrementally add a string to this hash
+ //
+ // @param {string} key A UTF-16 or ASCII string
+ // @return {object} this
+ MurmurHash3.prototype.hash = function(key) {
+ var h1, k1, i, top, len;
+
+ len = key.length;
+ this.len += len;
+
+ k1 = this.k1;
+ i = 0;
+ switch (this.rem) {
+ case 0: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) : 0;
+ case 1: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 8 : 0;
+ case 2: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 16 : 0;
+ case 3:
+ k1 ^= len > i ? (key.charCodeAt(i) & 0xff) << 24 : 0;
+ k1 ^= len > i ? (key.charCodeAt(i++) & 0xff00) >> 8 : 0;
+ }
+
+ this.rem = (len + this.rem) & 3; // & 3 is same as % 4
+ len -= this.rem;
+ if (len > 0) {
+ h1 = this.h1;
+ while (1) {
+ k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff;
+ k1 = (k1 << 15) | (k1 >>> 17);
+ k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff;
+
+ h1 ^= k1;
+ h1 = (h1 << 13) | (h1 >>> 19);
+ h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff;
+
+ if (i >= len) {
+ break;
+ }
+
+ k1 = ((key.charCodeAt(i++) & 0xffff)) ^
+ ((key.charCodeAt(i++) & 0xffff) << 8) ^
+ ((key.charCodeAt(i++) & 0xffff) << 16);
+ top = key.charCodeAt(i++);
+ k1 ^= ((top & 0xff) << 24) ^
+ ((top & 0xff00) >> 8);
+ }
+
+ k1 = 0;
+ switch (this.rem) {
+ case 3: k1 ^= (key.charCodeAt(i + 2) & 0xffff) << 16;
+ case 2: k1 ^= (key.charCodeAt(i + 1) & 0xffff) << 8;
+ case 1: k1 ^= (key.charCodeAt(i) & 0xffff);
+ }
+
+ this.h1 = h1;
+ }
+
+ this.k1 = k1;
+ return this;
+ };
+
+ // Get the result of this hash
+ //
+ // @return {number} The 32-bit hash
+ MurmurHash3.prototype.result = function() {
+ var k1, h1;
+
+ k1 = this.k1;
+ h1 = this.h1;
+
+ if (k1 > 0) {
+ k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff;
+ k1 = (k1 << 15) | (k1 >>> 17);
+ k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff;
+ h1 ^= k1;
+ }
+
+ h1 ^= this.len;
+
+ h1 ^= h1 >>> 16;
+ h1 = (h1 * 0xca6b + (h1 & 0xffff) * 0x85eb0000) & 0xffffffff;
+ h1 ^= h1 >>> 13;
+ h1 = (h1 * 0xae35 + (h1 & 0xffff) * 0xc2b20000) & 0xffffffff;
+ h1 ^= h1 >>> 16;
+
+ return h1 >>> 0;
+ };
+
+ // Reset the hash object for reuse
+ //
+ // @param {number} seed An optional positive integer
+ MurmurHash3.prototype.reset = function(seed) {
+ this.h1 = typeof seed === 'number' ? seed : 0;
+ this.rem = this.k1 = this.len = 0;
+ return this;
+ };
+
+ // A cached object to use. This can be safely used if you're in a single-
+ // threaded environment, otherwise you need to create new hashes to use.
+ cache = new MurmurHash3();
+
+ if (typeof(module) != 'undefined') {
+ module.exports = MurmurHash3;
+ } else {
+ this.MurmurHash3 = MurmurHash3;
+ }
+}());
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/imurmurhash.min.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/imurmurhash.min.js
new file mode 100644
index 000000000..dc0ee88d6
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/imurmurhash.min.js
@@ -0,0 +1,12 @@
+/**
+ * @preserve
+ * JS Implementation of incremental MurmurHash3 (r150) (as of May 10, 2013)
+ *
+ * @author Jens Taylor
+ * @see http://github.com/homebrewing/brauhaus-diff
+ * @author Gary Court
+ * @see http://github.com/garycourt/murmurhash-js
+ * @author Austin Appleby
+ * @see http://sites.google.com/site/murmurhash/
+ */
+!function(){function t(h,r){var s=this instanceof t?this:e;return s.reset(r),"string"==typeof h&&h.length>0&&s.hash(h),s!==this?s:void 0}var e;t.prototype.hash=function(t){var e,h,r,s,i;switch(i=t.length,this.len+=i,h=this.k1,r=0,this.rem){case 0:h^=i>r?65535&t.charCodeAt(r++):0;case 1:h^=i>r?(65535&t.charCodeAt(r++))<<8:0;case 2:h^=i>r?(65535&t.charCodeAt(r++))<<16:0;case 3:h^=i>r?(255&t.charCodeAt(r))<<24:0,h^=i>r?(65280&t.charCodeAt(r++))>>8:0}if(this.rem=3&i+this.rem,i-=this.rem,i>0){for(e=this.h1;;){if(h=4294967295&11601*h+3432906752*(65535&h),h=h<<15|h>>>17,h=4294967295&13715*h+461832192*(65535&h),e^=h,e=e<<13|e>>>19,e=4294967295&5*e+3864292196,r>=i)break;h=65535&t.charCodeAt(r++)^(65535&t.charCodeAt(r++))<<8^(65535&t.charCodeAt(r++))<<16,s=t.charCodeAt(r++),h^=(255&s)<<24^(65280&s)>>8}switch(h=0,this.rem){case 3:h^=(65535&t.charCodeAt(r+2))<<16;case 2:h^=(65535&t.charCodeAt(r+1))<<8;case 1:h^=65535&t.charCodeAt(r)}this.h1=e}return this.k1=h,this},t.prototype.result=function(){var t,e;return t=this.k1,e=this.h1,t>0&&(t=4294967295&11601*t+3432906752*(65535&t),t=t<<15|t>>>17,t=4294967295&13715*t+461832192*(65535&t),e^=t),e^=this.len,e^=e>>>16,e=4294967295&51819*e+2246770688*(65535&e),e^=e>>>13,e=4294967295&44597*e+3266445312*(65535&e),e^=e>>>16,e>>>0},t.prototype.reset=function(t){return this.h1="number"==typeof t?t:0,this.rem=this.k1=this.len=0,this},e=new t,"undefined"!=typeof module?module.exports=t:this.MurmurHash3=t}();
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/package.json
new file mode 100644
index 000000000..8a93edb55
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/imurmurhash/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "imurmurhash",
+ "version": "0.1.4",
+ "description": "An incremental implementation of MurmurHash3",
+ "homepage": "https://github.com/jensyt/imurmurhash-js",
+ "main": "imurmurhash.js",
+ "files": [
+ "imurmurhash.js",
+ "imurmurhash.min.js",
+ "package.json",
+ "README.md"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jensyt/imurmurhash-js"
+ },
+ "bugs": {
+ "url": "https://github.com/jensyt/imurmurhash-js/issues"
+ },
+ "keywords": [
+ "murmur",
+ "murmurhash",
+ "murmurhash3",
+ "hash",
+ "incremental"
+ ],
+ "author": {
+ "name": "Jens Taylor",
+ "email": "jensyt@gmail.com",
+ "url": "https://github.com/homebrewing"
+ },
+ "license": "MIT",
+ "dependencies": {
+ },
+ "devDependencies": {
+ },
+ "engines": {
+ "node": ">=0.8.19"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/index.d.ts
new file mode 100644
index 000000000..118523115
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/index.d.ts
@@ -0,0 +1,42 @@
+declare namespace indentString {
+ interface Options {
+ /**
+ The string to use for the indent.
+
+ @default ' '
+ */
+ readonly indent?: string;
+
+ /**
+ Also indent empty lines.
+
+ @default false
+ */
+ readonly includeEmptyLines?: boolean;
+ }
+}
+
+/**
+Indent each line in a string.
+
+@param string - The string to indent.
+@param count - How many times you want `options.indent` repeated. Default: `1`.
+
+@example
+```
+import indentString = require('indent-string');
+
+indentString('Unicorns\nRainbows', 4);
+//=> ' Unicorns\n Rainbows'
+
+indentString('Unicorns\nRainbows', 4, {indent: '♥'});
+//=> '♥♥♥♥Unicorns\n♥♥♥♥Rainbows'
+```
+*/
+declare function indentString(
+ string: string,
+ count?: number,
+ options?: indentString.Options
+): string;
+
+export = indentString;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/index.js
new file mode 100644
index 000000000..e1ab804f2
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/index.js
@@ -0,0 +1,35 @@
+'use strict';
+
+module.exports = (string, count = 1, options) => {
+ options = {
+ indent: ' ',
+ includeEmptyLines: false,
+ ...options
+ };
+
+ if (typeof string !== 'string') {
+ throw new TypeError(
+ `Expected \`input\` to be a \`string\`, got \`${typeof string}\``
+ );
+ }
+
+ if (typeof count !== 'number') {
+ throw new TypeError(
+ `Expected \`count\` to be a \`number\`, got \`${typeof count}\``
+ );
+ }
+
+ if (typeof options.indent !== 'string') {
+ throw new TypeError(
+ `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\``
+ );
+ }
+
+ if (count === 0) {
+ return string;
+ }
+
+ const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm;
+
+ return string.replace(regex, options.indent.repeat(count));
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/license b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/license
new file mode 100644
index 000000000..e7af2f771
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/license
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/package.json
new file mode 100644
index 000000000..497bb83bb
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "indent-string",
+ "version": "4.0.0",
+ "description": "Indent each line in a string",
+ "license": "MIT",
+ "repository": "sindresorhus/indent-string",
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "scripts": {
+ "test": "xo && ava && tsd"
+ },
+ "files": [
+ "index.js",
+ "index.d.ts"
+ ],
+ "keywords": [
+ "indent",
+ "string",
+ "pad",
+ "align",
+ "line",
+ "text",
+ "each",
+ "every"
+ ],
+ "devDependencies": {
+ "ava": "^1.4.1",
+ "tsd": "^0.7.2",
+ "xo": "^0.24.0"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/readme.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/readme.md
new file mode 100644
index 000000000..49967de07
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/indent-string/readme.md
@@ -0,0 +1,70 @@
+# indent-string [](https://travis-ci.org/sindresorhus/indent-string)
+
+> Indent each line in a string
+
+
+## Install
+
+```
+$ npm install indent-string
+```
+
+
+## Usage
+
+```js
+const indentString = require('indent-string');
+
+indentString('Unicorns\nRainbows', 4);
+//=> ' Unicorns\n Rainbows'
+
+indentString('Unicorns\nRainbows', 4, {indent: '♥'});
+//=> '♥♥♥♥Unicorns\n♥♥♥♥Rainbows'
+```
+
+
+## API
+
+### indentString(string, [count], [options])
+
+#### string
+
+Type: `string`
+
+The string to indent.
+
+#### count
+
+Type: `number`
+Default: `1`
+
+How many times you want `options.indent` repeated.
+
+#### options
+
+Type: `object`
+
+##### indent
+
+Type: `string`
+Default: `' '`
+
+The string to use for the indent.
+
+##### includeEmptyLines
+
+Type: `boolean`
+Default: `false`
+
+Also indent empty lines.
+
+
+## Related
+
+- [indent-string-cli](https://github.com/sindresorhus/indent-string-cli) - CLI for this module
+- [strip-indent](https://github.com/sindresorhus/strip-indent) - Strip leading whitespace from every line in a string
+
+
+## License
+
+MIT © [Sindre Sorhus](https://sindresorhus.com)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/LICENSE
new file mode 100644
index 000000000..20a476254
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) npm, Inc. and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/README.md
new file mode 100644
index 000000000..146caf7b8
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/README.md
@@ -0,0 +1,41 @@
+# infer-owner
+
+Infer the owner of a path based on the owner of its nearest existing parent
+
+## USAGE
+
+```js
+const inferOwner = require('infer-owner')
+
+inferOwner('/some/cache/folder/file').then(owner => {
+ // owner is {uid, gid} that should be attached to
+ // the /some/cache/folder/file, based on ownership
+ // of /some/cache/folder, /some/cache, /some, or /,
+ // whichever is the first to exist
+})
+
+// same, but not async
+const owner = inferOwner.sync('/some/cache/folder/file')
+
+// results are cached! to reset the cache (eg, to change
+// permissions for whatever reason), do this:
+inferOwner.clearCache()
+```
+
+This module endeavors to be as performant as possible. Parallel requests
+for ownership of the same path will only stat the directories one time.
+
+## API
+
+* `inferOwner(path) -> Promise<{ uid, gid }>`
+
+ If the path exists, return its uid and gid. If it does not, look to
+ its parent, then its grandparent, and so on.
+
+* `inferOwner(path) -> { uid, gid }`
+
+ Sync form of `inferOwner(path)`.
+
+* `inferOwner.clearCache()`
+
+ Delete all cached ownership information and in-flight tracking.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/index.js
new file mode 100644
index 000000000..a7bddcbd2
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/index.js
@@ -0,0 +1,71 @@
+const cache = new Map()
+const fs = require('fs')
+const { dirname, resolve } = require('path')
+
+
+const lstat = path => new Promise((res, rej) =>
+ fs.lstat(path, (er, st) => er ? rej(er) : res(st)))
+
+const inferOwner = path => {
+ path = resolve(path)
+ if (cache.has(path))
+ return Promise.resolve(cache.get(path))
+
+ const statThen = st => {
+ const { uid, gid } = st
+ cache.set(path, { uid, gid })
+ return { uid, gid }
+ }
+ const parent = dirname(path)
+ const parentTrap = parent === path ? null : er => {
+ return inferOwner(parent).then((owner) => {
+ cache.set(path, owner)
+ return owner
+ })
+ }
+ return lstat(path).then(statThen, parentTrap)
+}
+
+const inferOwnerSync = path => {
+ path = resolve(path)
+ if (cache.has(path))
+ return cache.get(path)
+
+ const parent = dirname(path)
+
+ // avoid obscuring call site by re-throwing
+ // "catch" the error by returning from a finally,
+ // only if we're not at the root, and the parent call works.
+ let threw = true
+ try {
+ const st = fs.lstatSync(path)
+ threw = false
+ const { uid, gid } = st
+ cache.set(path, { uid, gid })
+ return { uid, gid }
+ } finally {
+ if (threw && parent !== path) {
+ const owner = inferOwnerSync(parent)
+ cache.set(path, owner)
+ return owner // eslint-disable-line no-unsafe-finally
+ }
+ }
+}
+
+const inflight = new Map()
+module.exports = path => {
+ path = resolve(path)
+ if (inflight.has(path))
+ return Promise.resolve(inflight.get(path))
+ const p = inferOwner(path).then(owner => {
+ inflight.delete(path)
+ return owner
+ })
+ inflight.set(path, p)
+ return p
+}
+module.exports.sync = inferOwnerSync
+module.exports.clearCache = () => {
+ cache.clear()
+ inflight.clear()
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/package.json
new file mode 100644
index 000000000..c4b2b6e6d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/infer-owner/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "infer-owner",
+ "version": "1.0.4",
+ "description": "Infer the owner of a path based on the owner of its nearest existing parent",
+ "author": "Isaac Z. Schlueter (https://izs.me)",
+ "license": "ISC",
+ "scripts": {
+ "test": "tap -J test/*.js --100",
+ "snap": "TAP_SNAPSHOT=1 tap -J test/*.js --100",
+ "preversion": "npm test",
+ "postversion": "npm publish",
+ "postpublish": "git push origin --follow-tags"
+ },
+ "devDependencies": {
+ "mutate-fs": "^2.1.1",
+ "tap": "^12.4.2"
+ },
+ "main": "index.js",
+ "repository": "https://github.com/npm/infer-owner",
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "index.js"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/LICENSE
new file mode 100644
index 000000000..05eeeb88c
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/README.md
new file mode 100644
index 000000000..6dc892917
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/README.md
@@ -0,0 +1,37 @@
+# inflight
+
+Add callbacks to requests in flight to avoid async duplication
+
+## USAGE
+
+```javascript
+var inflight = require('inflight')
+
+// some request that does some stuff
+function req(key, callback) {
+ // key is any random string. like a url or filename or whatever.
+ //
+ // will return either a falsey value, indicating that the
+ // request for this key is already in flight, or a new callback
+ // which when called will call all callbacks passed to inflightk
+ // with the same key
+ callback = inflight(key, callback)
+
+ // If we got a falsey value back, then there's already a req going
+ if (!callback) return
+
+ // this is where you'd fetch the url or whatever
+ // callback is also once()-ified, so it can safely be assigned
+ // to multiple events etc. First call wins.
+ setTimeout(function() {
+ callback(null, key)
+ }, 100)
+}
+
+// only assigns a single setTimeout
+// when it dings, all cbs get called
+req('foo', cb1)
+req('foo', cb2)
+req('foo', cb3)
+req('foo', cb4)
+```
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/inflight.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/inflight.js
new file mode 100644
index 000000000..48202b3ca
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/inflight.js
@@ -0,0 +1,54 @@
+var wrappy = require('wrappy')
+var reqs = Object.create(null)
+var once = require('once')
+
+module.exports = wrappy(inflight)
+
+function inflight (key, cb) {
+ if (reqs[key]) {
+ reqs[key].push(cb)
+ return null
+ } else {
+ reqs[key] = [cb]
+ return makeres(key)
+ }
+}
+
+function makeres (key) {
+ return once(function RES () {
+ var cbs = reqs[key]
+ var len = cbs.length
+ var args = slice(arguments)
+
+ // XXX It's somewhat ambiguous whether a new callback added in this
+ // pass should be queued for later execution if something in the
+ // list of callbacks throws, or if it should just be discarded.
+ // However, it's such an edge case that it hardly matters, and either
+ // choice is likely as surprising as the other.
+ // As it happens, we do go ahead and schedule it for later execution.
+ try {
+ for (var i = 0; i < len; i++) {
+ cbs[i].apply(null, args)
+ }
+ } finally {
+ if (cbs.length > len) {
+ // added more in the interim.
+ // de-zalgo, just in case, but don't call again.
+ cbs.splice(0, len)
+ process.nextTick(function () {
+ RES.apply(null, args)
+ })
+ } else {
+ delete reqs[key]
+ }
+ }
+ })
+}
+
+function slice (args) {
+ var length = args.length
+ var array = []
+
+ for (var i = 0; i < length; i++) array[i] = args[i]
+ return array
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/package.json
new file mode 100644
index 000000000..6084d3509
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inflight/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "inflight",
+ "version": "1.0.6",
+ "description": "Add callbacks to requests in flight to avoid async duplication",
+ "main": "inflight.js",
+ "files": [
+ "inflight.js"
+ ],
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ },
+ "devDependencies": {
+ "tap": "^7.1.2"
+ },
+ "scripts": {
+ "test": "tap test.js --100"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/npm/inflight.git"
+ },
+ "author": "Isaac Z. Schlueter (http://blog.izs.me/)",
+ "bugs": {
+ "url": "https://github.com/isaacs/inflight/issues"
+ },
+ "homepage": "https://github.com/isaacs/inflight",
+ "license": "ISC"
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/LICENSE
new file mode 100644
index 000000000..dea3013d6
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/LICENSE
@@ -0,0 +1,16 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/README.md
new file mode 100644
index 000000000..b1c566585
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/README.md
@@ -0,0 +1,42 @@
+Browser-friendly inheritance fully compatible with standard node.js
+[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).
+
+This package exports standard `inherits` from node.js `util` module in
+node environment, but also provides alternative browser-friendly
+implementation through [browser
+field](https://gist.github.com/shtylman/4339901). Alternative
+implementation is a literal copy of standard one located in standalone
+module to avoid requiring of `util`. It also has a shim for old
+browsers with no `Object.create` support.
+
+While keeping you sure you are using standard `inherits`
+implementation in node.js environment, it allows bundlers such as
+[browserify](https://github.com/substack/node-browserify) to not
+include full `util` package to your client code if all you need is
+just `inherits` function. It worth, because browser shim for `util`
+package is large and `inherits` is often the single function you need
+from it.
+
+It's recommended to use this package instead of
+`require('util').inherits` for any code that has chances to be used
+not only in node.js but in browser too.
+
+## usage
+
+```js
+var inherits = require('inherits');
+// then use exactly as the standard one
+```
+
+## note on version ~1.0
+
+Version ~1.0 had completely different motivation and is not compatible
+neither with 2.0 nor with standard node.js `inherits`.
+
+If you are using version ~1.0 and planning to switch to ~2.0, be
+careful:
+
+* new version uses `super_` instead of `super` for referencing
+ superclass
+* new version overwrites current prototype while old one preserves any
+ existing fields on it
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/inherits.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/inherits.js
new file mode 100644
index 000000000..f71f2d932
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/inherits.js
@@ -0,0 +1,9 @@
+try {
+ var util = require('util');
+ /* istanbul ignore next */
+ if (typeof util.inherits !== 'function') throw '';
+ module.exports = util.inherits;
+} catch (e) {
+ /* istanbul ignore next */
+ module.exports = require('./inherits_browser.js');
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/inherits_browser.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/inherits_browser.js
new file mode 100644
index 000000000..86bbb3dc2
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/inherits_browser.js
@@ -0,0 +1,27 @@
+if (typeof Object.create === 'function') {
+ // implementation from standard node.js 'util' module
+ module.exports = function inherits(ctor, superCtor) {
+ if (superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ })
+ }
+ };
+} else {
+ // old school shim for old browsers
+ module.exports = function inherits(ctor, superCtor) {
+ if (superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/package.json
new file mode 100644
index 000000000..37b4366b8
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/inherits/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "inherits",
+ "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()",
+ "version": "2.0.4",
+ "keywords": [
+ "inheritance",
+ "class",
+ "klass",
+ "oop",
+ "object-oriented",
+ "inherits",
+ "browser",
+ "browserify"
+ ],
+ "main": "./inherits.js",
+ "browser": "./inherits_browser.js",
+ "repository": "git://github.com/isaacs/inherits",
+ "license": "ISC",
+ "scripts": {
+ "test": "tap"
+ },
+ "devDependencies": {
+ "tap": "^14.2.4"
+ },
+ "files": [
+ "inherits.js",
+ "inherits_browser.js"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/LICENSE
new file mode 100644
index 000000000..19129e315
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/README.md
new file mode 100644
index 000000000..33df25829
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/README.md
@@ -0,0 +1,102 @@
+An ini format parser and serializer for node.
+
+Sections are treated as nested objects. Items before the first
+heading are saved on the object directly.
+
+## Usage
+
+Consider an ini-file `config.ini` that looks like this:
+
+ ; this comment is being ignored
+ scope = global
+
+ [database]
+ user = dbuser
+ password = dbpassword
+ database = use_this_database
+
+ [paths.default]
+ datadir = /var/lib/data
+ array[] = first value
+ array[] = second value
+ array[] = third value
+
+You can read, manipulate and write the ini-file like so:
+
+ var fs = require('fs')
+ , ini = require('ini')
+
+ var config = ini.parse(fs.readFileSync('./config.ini', 'utf-8'))
+
+ config.scope = 'local'
+ config.database.database = 'use_another_database'
+ config.paths.default.tmpdir = '/tmp'
+ delete config.paths.default.datadir
+ config.paths.default.array.push('fourth value')
+
+ fs.writeFileSync('./config_modified.ini', ini.stringify(config, { section: 'section' }))
+
+This will result in a file called `config_modified.ini` being written
+to the filesystem with the following content:
+
+ [section]
+ scope=local
+ [section.database]
+ user=dbuser
+ password=dbpassword
+ database=use_another_database
+ [section.paths.default]
+ tmpdir=/tmp
+ array[]=first value
+ array[]=second value
+ array[]=third value
+ array[]=fourth value
+
+
+## API
+
+### decode(inistring)
+
+Decode the ini-style formatted `inistring` into a nested object.
+
+### parse(inistring)
+
+Alias for `decode(inistring)`
+
+### encode(object, [options])
+
+Encode the object `object` into an ini-style formatted string. If the
+optional parameter `section` is given, then all top-level properties
+of the object are put into this section and the `section`-string is
+prepended to all sub-sections, see the usage example above.
+
+The `options` object may contain the following:
+
+* `section` A string which will be the first `section` in the encoded
+ ini data. Defaults to none.
+* `whitespace` Boolean to specify whether to put whitespace around the
+ `=` character. By default, whitespace is omitted, to be friendly to
+ some persnickety old parsers that don't tolerate it well. But some
+ find that it's more human-readable and pretty with the whitespace.
+
+For backwards compatibility reasons, if a `string` options is passed
+in, then it is assumed to be the `section` value.
+
+### stringify(object, [options])
+
+Alias for `encode(object, [options])`
+
+### safe(val)
+
+Escapes the string `val` such that it is safe to be used as a key or
+value in an ini-file. Basically escapes quotes. For example
+
+ ini.safe('"unsafe string"')
+
+would result in
+
+ "\"unsafe string\""
+
+### unsafe(val)
+
+Unescapes the string `val`
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/ini.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/ini.js
new file mode 100644
index 000000000..b576f08d7
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/ini.js
@@ -0,0 +1,206 @@
+exports.parse = exports.decode = decode
+
+exports.stringify = exports.encode = encode
+
+exports.safe = safe
+exports.unsafe = unsafe
+
+var eol = typeof process !== 'undefined' &&
+ process.platform === 'win32' ? '\r\n' : '\n'
+
+function encode (obj, opt) {
+ var children = []
+ var out = ''
+
+ if (typeof opt === 'string') {
+ opt = {
+ section: opt,
+ whitespace: false,
+ }
+ } else {
+ opt = opt || {}
+ opt.whitespace = opt.whitespace === true
+ }
+
+ var separator = opt.whitespace ? ' = ' : '='
+
+ Object.keys(obj).forEach(function (k, _, __) {
+ var val = obj[k]
+ if (val && Array.isArray(val)) {
+ val.forEach(function (item) {
+ out += safe(k + '[]') + separator + safe(item) + '\n'
+ })
+ } else if (val && typeof val === 'object')
+ children.push(k)
+ else
+ out += safe(k) + separator + safe(val) + eol
+ })
+
+ if (opt.section && out.length)
+ out = '[' + safe(opt.section) + ']' + eol + out
+
+ children.forEach(function (k, _, __) {
+ var nk = dotSplit(k).join('\\.')
+ var section = (opt.section ? opt.section + '.' : '') + nk
+ var child = encode(obj[k], {
+ section: section,
+ whitespace: opt.whitespace,
+ })
+ if (out.length && child.length)
+ out += eol
+
+ out += child
+ })
+
+ return out
+}
+
+function dotSplit (str) {
+ return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002')
+ .replace(/\\\./g, '\u0001')
+ .split(/\./).map(function (part) {
+ return part.replace(/\1/g, '\\.')
+ .replace(/\2LITERAL\\1LITERAL\2/g, '\u0001')
+ })
+}
+
+function decode (str) {
+ var out = {}
+ var p = out
+ var section = null
+ // section |key = value
+ var re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i
+ var lines = str.split(/[\r\n]+/g)
+
+ lines.forEach(function (line, _, __) {
+ if (!line || line.match(/^\s*[;#]/))
+ return
+ var match = line.match(re)
+ if (!match)
+ return
+ if (match[1] !== undefined) {
+ section = unsafe(match[1])
+ if (section === '__proto__') {
+ // not allowed
+ // keep parsing the section, but don't attach it.
+ p = {}
+ return
+ }
+ p = out[section] = out[section] || {}
+ return
+ }
+ var key = unsafe(match[2])
+ if (key === '__proto__')
+ return
+ var value = match[3] ? unsafe(match[4]) : true
+ switch (value) {
+ case 'true':
+ case 'false':
+ case 'null': value = JSON.parse(value)
+ }
+
+ // Convert keys with '[]' suffix to an array
+ if (key.length > 2 && key.slice(-2) === '[]') {
+ key = key.substring(0, key.length - 2)
+ if (key === '__proto__')
+ return
+ if (!p[key])
+ p[key] = []
+ else if (!Array.isArray(p[key]))
+ p[key] = [p[key]]
+ }
+
+ // safeguard against resetting a previously defined
+ // array by accidentally forgetting the brackets
+ if (Array.isArray(p[key]))
+ p[key].push(value)
+ else
+ p[key] = value
+ })
+
+ // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}}
+ // use a filter to return the keys that have to be deleted.
+ Object.keys(out).filter(function (k, _, __) {
+ if (!out[k] ||
+ typeof out[k] !== 'object' ||
+ Array.isArray(out[k]))
+ return false
+
+ // see if the parent section is also an object.
+ // if so, add it to that, and mark this one for deletion
+ var parts = dotSplit(k)
+ var p = out
+ var l = parts.pop()
+ var nl = l.replace(/\\\./g, '.')
+ parts.forEach(function (part, _, __) {
+ if (part === '__proto__')
+ return
+ if (!p[part] || typeof p[part] !== 'object')
+ p[part] = {}
+ p = p[part]
+ })
+ if (p === out && nl === l)
+ return false
+
+ p[nl] = out[k]
+ return true
+ }).forEach(function (del, _, __) {
+ delete out[del]
+ })
+
+ return out
+}
+
+function isQuoted (val) {
+ return (val.charAt(0) === '"' && val.slice(-1) === '"') ||
+ (val.charAt(0) === "'" && val.slice(-1) === "'")
+}
+
+function safe (val) {
+ return (typeof val !== 'string' ||
+ val.match(/[=\r\n]/) ||
+ val.match(/^\[/) ||
+ (val.length > 1 &&
+ isQuoted(val)) ||
+ val !== val.trim())
+ ? JSON.stringify(val)
+ : val.replace(/;/g, '\\;').replace(/#/g, '\\#')
+}
+
+function unsafe (val, doUnesc) {
+ val = (val || '').trim()
+ if (isQuoted(val)) {
+ // remove the single quotes before calling JSON.parse
+ if (val.charAt(0) === "'")
+ val = val.substr(1, val.length - 2)
+
+ try {
+ val = JSON.parse(val)
+ } catch (_) {}
+ } else {
+ // walk the val to find the first not-escaped ; character
+ var esc = false
+ var unesc = ''
+ for (var i = 0, l = val.length; i < l; i++) {
+ var c = val.charAt(i)
+ if (esc) {
+ if ('\\;#'.indexOf(c) !== -1)
+ unesc += c
+ else
+ unesc += '\\' + c
+
+ esc = false
+ } else if (';#'.indexOf(c) !== -1)
+ break
+ else if (c === '\\')
+ esc = true
+ else
+ unesc += c
+ }
+ if (esc)
+ unesc += '\\'
+
+ return unesc.trim()
+ }
+ return val
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/package.json
new file mode 100644
index 000000000..c830a3556
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ini/package.json
@@ -0,0 +1,33 @@
+{
+ "author": "Isaac Z. Schlueter (http://blog.izs.me/)",
+ "name": "ini",
+ "description": "An ini encoder/decoder for node",
+ "version": "1.3.8",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/ini.git"
+ },
+ "main": "ini.js",
+ "scripts": {
+ "eslint": "eslint",
+ "lint": "npm run eslint -- ini.js test/*.js",
+ "lintfix": "npm run lint -- --fix",
+ "test": "tap",
+ "posttest": "npm run lint",
+ "preversion": "npm test",
+ "postversion": "npm publish",
+ "prepublishOnly": "git push origin --follow-tags"
+ },
+ "devDependencies": {
+ "eslint": "^7.9.0",
+ "eslint-plugin-import": "^2.22.0",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-promise": "^4.2.1",
+ "eslint-plugin-standard": "^4.0.1",
+ "tap": "14"
+ },
+ "license": "ISC",
+ "files": [
+ "ini.js"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/CHANGELOG b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/CHANGELOG
new file mode 100644
index 000000000..cbc8a8afd
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/CHANGELOG
@@ -0,0 +1,115 @@
+v1.0.3:
+ date: 2017-04-18
+ changes:
+ - fix buble support
+v1.0.2:
+ date: 2017-03-29
+ changes:
+ - add support for coffeescript (now with no hyphen)
+v1.0.1:
+ date: 2016-05-01
+ changes:
+ - add support for buble
+v1.0.0:
+ date: 2015-11-18
+ changes:
+ - add support for babel-register
+ - go stable!
+v0.6.6:
+ date: 2015-09-21
+ changes:
+ - add support for ts-node (formerly typescript-node)
+v0.6.5:
+ date: 2015-07-22
+ changes:
+ - add support for typescript 1.5 via typescript-node
+v0.6.4:
+ date: 2015-07-07
+ changes:
+ - add support for earlgrey
+v0.6.3:
+ date: 2015-07-03
+ changes:
+ - prefer babel/core to babel
+v0.6.2:
+ date: 2015-05-20
+ changes:
+ - update module list for iced coffee-script
+v0.6.1:
+ date: 2015-05-20
+ changes:
+ - Fix toml loader.
+v0.6.0:
+ date: 2015-05-19
+ changes:
+ - Combine fallbacks and loaders into `extensions`.
+ - Provide implementation guidance.
+v0.5.1:
+ date: 2015-03-01
+ changes:
+ - Add support for CirruScript.
+v0.5.0:
+ date: 2015-02-27
+ changes:
+ - Refactor es6 support via Babel (formerly 6to5)
+v0.4.3:
+ date: 2015-02-09
+ changes:
+ - Switch support from typescript-require to typescript-register.
+v0.4.2:
+ date: 2015-01-16
+ changes:
+ - Add support for wisp.
+v0.4.1:
+ date: 2015-01-10
+ changes:
+ - Add support for 6to5 (es6)
+v0.4.0:
+ date: 2014-01-09
+ changes:
+ - Add support for fallback (legacy) modules
+ - Add support for module configurations
+v0.3.10:
+ date: 2014-12-17
+ changes:
+ - Add support for json5.
+v0.3.9:
+ date: 2014-12-08
+ changes:
+ - Add support for literate iced coffee.
+v0.3.8:
+ date: 2014-11-20
+ changes:
+ - Add support for [cjsx](https://github.com/jsdf/coffee-react).
+v0.3.7:
+ date: 2014-09-08
+ changes:
+ - Add support for [TypeScript](http://www.typescriptlang.org/).
+v0.3.6:
+ date: 2014-08-25
+ changes:
+ - Add support for coffee.md.
+v0.3.5:
+ date: 2014-07-03
+ changes:
+ - Add support for jsx.
+v0.3.4:
+ date: 2014-06-27
+ changes:
+ - Make .js first jsVariant entry.
+v0.3.3:
+ date: 2014-06-02
+ changes:
+ - Fix casing on livescript dependency.
+v0.3.0:
+ date: 2014-04-20
+ changes:
+ - Simplify loading of coffee-script and iced-coffee-script.
+v0.2.0:
+ date: 2014-04-20
+ changes:
+ - Move module loading into rechoir.
+v0.1.0:
+ date: 2014-04-20
+ changes:
+ - Initial public release.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/LICENSE
new file mode 100644
index 000000000..7d7525daa
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2014-2018 Tyler Kellen , Blaine Bublitz , and Eric Schoffstall
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/README.md
new file mode 100644
index 000000000..f56410c09
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/README.md
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+# interpret
+
+[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Travis Build Status][travis-image]][travis-url] [![AppVeyor Build Status][appveyor-image]][appveyor-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Gitter chat][gitter-image]][gitter-url]
+
+A dictionary of file extensions and associated module loaders.
+
+## What is it
+This is used by [Liftoff](http://github.com/tkellen/node-liftoff) to automatically require dependencies for configuration files, and by [rechoir](http://github.com/tkellen/node-rechoir) for registering module loaders.
+
+## interpret for enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of interpret and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-interpret?utm_source=npm-interpret&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
+
+## API
+
+### extensions
+Map file types to modules which provide a [require.extensions] loader.
+
+```js
+{
+ '.babel.js': [
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ {
+ module: 'babel-register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel-core/register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ ],
+ '.babel.ts': [
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.ts',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ ],
+ '.buble.js': 'buble/register',
+ '.cirru': 'cirru-script/lib/register',
+ '.cjsx': 'node-cjsx/register',
+ '.co': 'coco',
+ '.coffee': ['coffeescript/register', 'coffee-script/register', 'coffeescript', 'coffee-script'],
+ '.coffee.md': ['coffeescript/register', 'coffee-script/register', 'coffeescript', 'coffee-script'],
+ '.csv': 'require-csv',
+ '.eg': 'earlgrey/register',
+ '.esm.js': {
+ module: 'esm',
+ register: function(hook) {
+ // register on .js extension due to https://github.com/joyent/node/blob/v0.12.0/lib/module.js#L353
+ // which only captures the final extension (.babel.js -> .js)
+ var esmLoader = hook(module);
+ require.extensions['.js'] = esmLoader('module')._extensions['.js'];
+ },
+ },
+ '.iced': ['iced-coffee-script/register', 'iced-coffee-script'],
+ '.iced.md': 'iced-coffee-script/register',
+ '.ini': 'require-ini',
+ '.js': null,
+ '.json': null,
+ '.json5': ['json5/lib/register', 'json5/lib/require'],
+ '.jsx': [
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ {
+ module: 'babel-register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel-core/register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'node-jsx',
+ register: function(hook) {
+ hook.install({ extension: '.jsx', harmony: true });
+ },
+ },
+ ],
+ '.litcoffee': ['coffeescript/register', 'coffee-script/register', 'coffeescript', 'coffee-script'],
+ '.liticed': 'iced-coffee-script/register',
+ '.ls': ['livescript', 'LiveScript'],
+ '.mjs': '/absolute/path/to/interpret/mjs-stub.js',
+ '.node': null,
+ '.toml': {
+ module: 'toml-require',
+ register: function(hook) {
+ hook.install();
+ },
+ },
+ '.ts': [
+ 'ts-node/register',
+ 'typescript-node/register',
+ 'typescript-register',
+ 'typescript-require',
+ 'sucrase/register/ts',
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.ts',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ ],
+ '.tsx': [
+ 'ts-node/register',
+ 'typescript-node/register',
+ 'sucrase/register',
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.tsx',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ ],
+ '.wisp': 'wisp/engine/node',
+ '.xml': 'require-xml',
+ '.yaml': 'require-yaml',
+ '.yml': 'require-yaml',
+}
+```
+
+### jsVariants
+Same as above, but only include the extensions which are javascript variants.
+
+## How to use it
+
+Consumers should use the exported `extensions` or `jsVariants` object to determine which module should be loaded for a given extension. If a matching extension is found, consumers should do the following:
+
+1. If the value is null, do nothing.
+
+2. If the value is a string, try to require it.
+
+3. If the value is an object, try to require the `module` property. If successful, the `register` property (a function) should be called with the module passed as the first argument.
+
+4. If the value is an array, iterate over it, attempting step #2 or #3 until one of the attempts does not throw.
+
+[require.extensions]: http://nodejs.org/api/globals.html#globals_require_extensions
+
+[downloads-image]: http://img.shields.io/npm/dm/interpret.svg
+[npm-url]: https://www.npmjs.com/package/interpret
+[npm-image]: http://img.shields.io/npm/v/interpret.svg
+
+[travis-url]: https://travis-ci.org/gulpjs/interpret
+[travis-image]: http://img.shields.io/travis/gulpjs/interpret.svg?label=travis-ci
+
+[appveyor-url]: https://ci.appveyor.com/project/gulpjs/interpret
+[appveyor-image]: https://img.shields.io/appveyor/ci/gulpjs/interpret.svg?label=appveyor
+
+[coveralls-url]: https://coveralls.io/r/gulpjs/interpret
+[coveralls-image]: http://img.shields.io/coveralls/gulpjs/interpret/master.svg
+
+[gitter-url]: https://gitter.im/gulpjs/gulp
+[gitter-image]: https://badges.gitter.im/gulpjs/gulp.svg
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/index.js
new file mode 100644
index 000000000..2a2b82975
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/index.js
@@ -0,0 +1,211 @@
+var path = require('path');
+
+var endsInBabelJs = /\.babel\.[jt]s(x)$/;
+
+var mjsStub = path.join(__dirname, 'mjs-stub');
+
+function ignoreNonBabelAndNodeModules(file) {
+ return !endsInBabelJs.test(file) &&
+ path.relative(process.cwd(), file).split(path.sep).indexOf('node_modules') >= 0;
+}
+
+var extensions = {
+ '.babel.js': [
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ {
+ module: 'babel-register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel-core/register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.js',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ ],
+ '.babel.ts': [
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.ts',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ ],
+ '.buble.js': 'buble/register',
+ '.cirru': 'cirru-script/lib/register',
+ '.cjsx': 'node-cjsx/register',
+ '.co': 'coco',
+ '.coffee': ['coffeescript/register', 'coffee-script/register', 'coffeescript', 'coffee-script'],
+ '.coffee.md': ['coffeescript/register', 'coffee-script/register', 'coffeescript', 'coffee-script'],
+ '.csv': 'require-csv',
+ '.eg': 'earlgrey/register',
+ '.esm.js': {
+ module: 'esm',
+ register: function(hook) {
+ // register on .js extension due to https://github.com/joyent/node/blob/v0.12.0/lib/module.js#L353
+ // which only captures the final extension (.babel.js -> .js)
+ var esmLoader = hook(module);
+ require.extensions['.js'] = esmLoader('module')._extensions['.js'];
+ },
+ },
+ '.iced': ['iced-coffee-script/register', 'iced-coffee-script'],
+ '.iced.md': 'iced-coffee-script/register',
+ '.ini': 'require-ini',
+ '.js': null,
+ '.json': null,
+ '.json5': ['json5/lib/register', 'json5/lib/require'],
+ '.jsx': [
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ {
+ module: 'babel-register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel-core/register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.jsx',
+ ignore: ignoreNonBabelAndNodeModules,
+ });
+ },
+ },
+ {
+ module: 'node-jsx',
+ register: function(hook) {
+ hook.install({ extension: '.jsx', harmony: true });
+ },
+ },
+ ],
+ '.litcoffee': ['coffeescript/register', 'coffee-script/register', 'coffeescript', 'coffee-script'],
+ '.liticed': 'iced-coffee-script/register',
+ '.ls': ['livescript', 'LiveScript'],
+ '.mjs': mjsStub,
+ '.node': null,
+ '.toml': {
+ module: 'toml-require',
+ register: function(hook) {
+ hook.install();
+ },
+ },
+ '.ts': [
+ 'ts-node/register',
+ 'typescript-node/register',
+ 'typescript-register',
+ 'typescript-require',
+ 'sucrase/register/ts',
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.ts',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ ],
+ '.tsx': [
+ 'ts-node/register',
+ 'typescript-node/register',
+ 'sucrase/register',
+ {
+ module: '@babel/register',
+ register: function(hook) {
+ hook({
+ extensions: '.tsx',
+ rootMode: 'upward-optional',
+ ignore: [ignoreNonBabelAndNodeModules],
+ });
+ },
+ },
+ ],
+ '.wisp': 'wisp/engine/node',
+ '.xml': 'require-xml',
+ '.yaml': 'require-yaml',
+ '.yml': 'require-yaml',
+};
+
+var jsVariantExtensions = [
+ '.js',
+ '.babel.js',
+ '.babel.ts',
+ '.buble.js',
+ '.cirru',
+ '.cjsx',
+ '.co',
+ '.coffee',
+ '.coffee.md',
+ '.eg',
+ '.esm.js',
+ '.iced',
+ '.iced.md',
+ '.jsx',
+ '.litcoffee',
+ '.liticed',
+ '.ls',
+ '.mjs',
+ '.ts',
+ '.tsx',
+ '.wisp',
+];
+
+module.exports = {
+ extensions: extensions,
+ jsVariants: jsVariantExtensions.reduce(function(result, ext) {
+ result[ext] = extensions[ext];
+ return result;
+ }, {}),
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/mjs-stub.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/mjs-stub.js
new file mode 100644
index 000000000..6a1af9568
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/mjs-stub.js
@@ -0,0 +1 @@
+require.extensions['.mjs'] = null;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/package.json
new file mode 100644
index 000000000..cc05601f4
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/interpret/package.json
@@ -0,0 +1,75 @@
+{
+ "name": "interpret",
+ "version": "2.2.0",
+ "description": "A dictionary of file extensions and associated module loaders.",
+ "author": "Gulp Team (http://gulpjs.com/)",
+ "contributors": [
+ "Blaine Bublitz ",
+ "Tyler Kellen (http://goingslowly.com/)"
+ ],
+ "repository": "gulpjs/interpret",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "main": "index.js",
+ "files": [
+ "LICENSE",
+ "index.js",
+ "mjs-stub.js"
+ ],
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "rm -rf tmp/ && npm run lint",
+ "test": "mocha --async-only",
+ "cover": "nyc --reporter=lcov --reporter=text-summary npm test",
+ "coveralls": "nyc --reporter=text-lcov npm test | coveralls"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "coveralls": "github:phated/node-coveralls#2.x",
+ "eslint": "^2.13.0",
+ "eslint-config-gulp": "^3.0.1",
+ "expect": "^1.20.2",
+ "mocha": "^3.5.3",
+ "nyc": "^10.3.2",
+ "parse-node-version": "^1.0.0",
+ "rechoir": "^0.7.0",
+ "shelljs": "0.7.5",
+ "trash-cli": "^3.0.0"
+ },
+ "keywords": [
+ "cirru-script",
+ "cjsx",
+ "co",
+ "coco",
+ "coffee",
+ "coffee-script",
+ "coffee.md",
+ "coffeescript",
+ "csv",
+ "earlgrey",
+ "es",
+ "es6",
+ "iced",
+ "iced.md",
+ "iced-coffee-script",
+ "ini",
+ "js",
+ "json",
+ "json5",
+ "jsx",
+ "react",
+ "litcoffee",
+ "liticed",
+ "ls",
+ "livescript",
+ "toml",
+ "ts",
+ "typescript",
+ "wisp",
+ "xml",
+ "yaml",
+ "yml"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/LICENSE
new file mode 100644
index 000000000..ec79adb0c
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2011 by Beau Gunderson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/README.md
new file mode 100644
index 000000000..93ccb412d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/README.md
@@ -0,0 +1,105 @@
+[](https://dl.circleci.com/status-badge/redirect/circleci/9fJmTZfn8d8p7GtVt688PY/JjriGjhcxBD6zYKygMZaet/tree/master)
+[![codecov]](https://codecov.io/github/beaugunderson/ip-address?branch=master)
+[![downloads]](https://www.npmjs.com/package/ip-address)
+[![npm]](https://www.npmjs.com/package/ip-address)
+[![snyk]](https://snyk.io/test/github/beaugunderson/ip-address)
+
+[codecov]: https://codecov.io/github/beaugunderson/ip-address/coverage.svg?branch=master
+[downloads]: https://img.shields.io/npm/dm/ip-address.svg
+[npm]: https://img.shields.io/npm/v/ip-address.svg
+[snyk]: https://snyk.io/test/github/beaugunderson/ip-address/badge.svg
+
+## ip-address
+
+`ip-address` is a library for validating and manipulating IPv4 and IPv6
+addresses in JavaScript.
+
+### Upgrading from 9.x to 10.x
+
+The dependency on `jsbn` was removed thanks to
+[michal-kocarek](https://github.com/michal-kocarek). Thanks Michal! For
+clarity, all methods with BigInteger in the name were renamed to BigInt.
+
+#### Breaking changes
+
+- `#fromBigInteger()` → `#fromBigInt()`; now returns a native BigInt
+- `#bigInteger()` → `#bigInt()`; now returns a native BigInt
+
+### Documentation
+
+Documentation is available at [ip-address.js.org](http://ip-address.js.org/).
+
+### Examples
+
+```js
+var Address6 = require('ip-address').Address6;
+
+var address = new Address6('2001:0:ce49:7601:e866:efff:62c3:fffe');
+
+var teredo = address.inspectTeredo();
+
+teredo.client4; // '157.60.0.1'
+```
+
+### Features
+
+- Usable via CommonJS or ESM
+- Parsing of all IPv6 notations
+- Parsing of IPv6 addresses and ports from URLs with `Address6.fromURL(url)`
+- Validity checking
+- Decoding of the [Teredo
+ information](http://en.wikipedia.org/wiki/Teredo_tunneling#IPv6_addressing)
+ in an address
+- Whether one address is a valid subnet of another
+- What special properties a given address has (multicast prefix, unique
+ local address prefix, etc.)
+- Number of subnets of a certain size in a given address
+- Display methods
+ - Hex, binary, and decimal
+ - Canonical form
+ - Correct form
+ - IPv4-compatible (i.e. `::ffff:192.168.0.1`)
+- Works in [node](http://nodejs.org/) and the browser (with browserify)
+- ~1,600 test cases
+
+### Used by
+
+- [anon](https://github.com/edsu/anon) which powers
+ [@congressedits](https://twitter.com/congressedits), among
+ [many others](https://github.com/edsu/anon#community)
+- [base85](https://github.com/noseglid/base85): base85 encoding/decoding
+- [contrail-web-core](https://github.com/Juniper/contrail-web-core): part of
+ Contrail, a network virtualization solution made by Juniper Networks
+- [dhcpjs](https://github.com/apaprocki/node-dhcpjs): a DHCP client and server
+- [epochtalk](https://github.com/epochtalk/epochtalk): next generation forum
+ software
+- [geoip-web](https://github.com/tfrce/node-geoip-web): a server for
+ quickly geolocating IP addresses
+- [hexabus](https://github.com/mysmartgrid/hexabus): an IPv6-based home
+ automation bus
+- [hubot-deploy](https://github.com/atmos/hubot-deploy): GitHub Flow via hubot
+- [heroku-portscanner](https://github.com/robison/heroku-portscanner): nmap
+ hosted on Heroku
+- [ipfs-swarm](https://github.com/diasdavid/node-ipfs-swarm): a swarm
+ implementation based on IPFS
+- [javascript-x-server](https://github.com/GothAck/javascript-x-server): an X
+ server written in JavaScript
+- [libnmap](https://github.com/jas-/node-libnmap): a node API for nmap
+- [mail-io](https://github.com/mofux/mail-io): a lightweight SMTP server
+- [maxmind-db-reader](https://github.com/PaddeK/node-maxmind-db): a library for
+ reading MaxMind database files
+- [proxy-protocol-v2](https://github.com/ably/proxy-protocol-v2): a proxy
+ protocol encoder/decoder built by [Ably](https://www.ably.io/)
+- [Samsara](https://github.com/mariusGundersen/Samsara): a Docker web interface
+- [sis-api](https://github.com/sis-cmdb/sis-api): a configuration management
+ database API
+- [socks5-client](https://github.com/mattcg/socks5-client): a SOCKS v5 client
+- [socksified](https://github.com/vially/node-socksified): a SOCKS v5 client
+- [socksv5](https://github.com/mscdex/socksv5): a SOCKS v5 server/client
+- [ssdapi](https://github.com/rsolomou/ssdapi): an API created by the
+ University of Portsmouth
+- [SwitchyOmega](https://github.com/FelisCatus/SwitchyOmega): a [Chrome
+ extension](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)
+ for switching between multiple proxies with ~311k users!
+- [swiz](https://github.com/racker/node-swiz): a serialization framework built
+ and used by [Rackspace](http://www.rackspace.com/)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.d.ts
new file mode 100644
index 000000000..59b216516
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.d.ts
@@ -0,0 +1,5 @@
+export declare class AddressError extends Error {
+ parseMessage?: string;
+ constructor(message: string, parseMessage?: string);
+}
+//# sourceMappingURL=address-error.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.d.ts.map
new file mode 100644
index 000000000..0ef7fa696
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"address-error.d.ts","sourceRoot":"","sources":["../src/address-error.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;CAOnD"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.js
new file mode 100644
index 000000000..c178ae482
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.js
@@ -0,0 +1,12 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.AddressError = void 0;
+class AddressError extends Error {
+ constructor(message, parseMessage) {
+ super(message);
+ this.name = 'AddressError';
+ this.parseMessage = parseMessage;
+ }
+}
+exports.AddressError = AddressError;
+//# sourceMappingURL=address-error.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.js.map
new file mode 100644
index 000000000..5d71d1343
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/address-error.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"address-error.js","sourceRoot":"","sources":["../src/address-error.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAa,SAAQ,KAAK;IAGrC,YAAY,OAAe,EAAE,YAAqB;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QAEf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAE3B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;CACF;AAVD,oCAUC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.d.ts
new file mode 100644
index 000000000..a4250d881
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.d.ts
@@ -0,0 +1,15 @@
+import { Address4 } from './ipv4';
+import { Address6 } from './ipv6';
+export interface ReverseFormOptions {
+ omitSuffix?: boolean;
+}
+export declare function isInSubnet(this: Address4 | Address6, address: Address4 | Address6): boolean;
+export declare function isCorrect(defaultBits: number): (this: Address4 | Address6) => boolean;
+export declare function numberToPaddedHex(number: number): string;
+export declare function stringToPaddedHex(numberString: string): string;
+/**
+ * @param binaryValue Binary representation of a value (e.g. `10`)
+ * @param position Byte position, where 0 is the least significant bit
+ */
+export declare function testBit(binaryValue: string, position: number): boolean;
+//# sourceMappingURL=common.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.d.ts.map
new file mode 100644
index 000000000..d13705ad6
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,QAAQ,WAUjF;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,UACpB,QAAQ,GAAG,QAAQ,aAW3C;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,UAE/C;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,UAErD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAStE"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.js
new file mode 100644
index 000000000..273a01e28
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.js
@@ -0,0 +1,46 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.isInSubnet = isInSubnet;
+exports.isCorrect = isCorrect;
+exports.numberToPaddedHex = numberToPaddedHex;
+exports.stringToPaddedHex = stringToPaddedHex;
+exports.testBit = testBit;
+function isInSubnet(address) {
+ if (this.subnetMask < address.subnetMask) {
+ return false;
+ }
+ if (this.mask(address.subnetMask) === address.mask()) {
+ return true;
+ }
+ return false;
+}
+function isCorrect(defaultBits) {
+ return function () {
+ if (this.addressMinusSuffix !== this.correctForm()) {
+ return false;
+ }
+ if (this.subnetMask === defaultBits && !this.parsedSubnet) {
+ return true;
+ }
+ return this.parsedSubnet === String(this.subnetMask);
+ };
+}
+function numberToPaddedHex(number) {
+ return number.toString(16).padStart(2, '0');
+}
+function stringToPaddedHex(numberString) {
+ return numberToPaddedHex(parseInt(numberString, 10));
+}
+/**
+ * @param binaryValue Binary representation of a value (e.g. `10`)
+ * @param position Byte position, where 0 is the least significant bit
+ */
+function testBit(binaryValue, position) {
+ const { length } = binaryValue;
+ if (position > length) {
+ return false;
+ }
+ const positionInString = length - position;
+ return binaryValue.substring(positionInString, positionInString + 1) === '1';
+}
+//# sourceMappingURL=common.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.js.map
new file mode 100644
index 000000000..036ce661d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/common.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"common.js","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":";;AAOA,gCAUC;AAED,8BAYC;AAED,8CAEC;AAED,8CAEC;AAMD,0BASC;AA/CD,SAAgB,UAAU,CAA4B,OAA4B;IAChF,IAAI,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,SAAS,CAAC,WAAmB;IAC3C,OAAO;QACL,IAAI,IAAI,CAAC,kBAAkB,KAAK,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACnD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC,YAAY,KAAK,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,iBAAiB,CAAC,MAAc;IAC9C,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED,SAAgB,iBAAiB,CAAC,YAAoB;IACpD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,WAAmB,EAAE,QAAgB;IAC3D,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC;IAE/B,IAAI,QAAQ,GAAG,MAAM,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC3C,OAAO,WAAW,CAAC,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;AAC/E,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.d.ts
new file mode 100644
index 000000000..14f8fe002
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.d.ts
@@ -0,0 +1,8 @@
+export { Address4 } from './ipv4';
+export { Address6 } from './ipv6';
+export { AddressError } from './address-error';
+import * as helpers from './v6/helpers';
+export declare const v6: {
+ helpers: typeof helpers;
+};
+//# sourceMappingURL=ip-address.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.d.ts.map
new file mode 100644
index 000000000..7bcb98aba
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"ip-address.d.ts","sourceRoot":"","sources":["../src/ip-address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC,eAAO,MAAM,EAAE;;CAAc,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.js
new file mode 100644
index 000000000..84f348709
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.js
@@ -0,0 +1,35 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.v6 = exports.AddressError = exports.Address6 = exports.Address4 = void 0;
+var ipv4_1 = require("./ipv4");
+Object.defineProperty(exports, "Address4", { enumerable: true, get: function () { return ipv4_1.Address4; } });
+var ipv6_1 = require("./ipv6");
+Object.defineProperty(exports, "Address6", { enumerable: true, get: function () { return ipv6_1.Address6; } });
+var address_error_1 = require("./address-error");
+Object.defineProperty(exports, "AddressError", { enumerable: true, get: function () { return address_error_1.AddressError; } });
+const helpers = __importStar(require("./v6/helpers"));
+exports.v6 = { helpers };
+//# sourceMappingURL=ip-address.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.js.map
new file mode 100644
index 000000000..cb89ed368
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ip-address.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"ip-address.js","sourceRoot":"","sources":["../src/ip-address.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+BAAkC;AAAzB,gGAAA,QAAQ,OAAA;AACjB,+BAAkC;AAAzB,gGAAA,QAAQ,OAAA;AACjB,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAErB,sDAAwC;AAE3B,QAAA,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.d.ts
new file mode 100644
index 000000000..28edbaa5b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.d.ts
@@ -0,0 +1,209 @@
+import * as common from './common';
+/**
+ * Represents an IPv4 address
+ * @class Address4
+ * @param {string} address - An IPv4 address string
+ */
+export declare class Address4 {
+ address: string;
+ addressMinusSuffix?: string;
+ groups: number;
+ parsedAddress: string[];
+ parsedSubnet: string;
+ subnet: string;
+ subnetMask: number;
+ v4: boolean;
+ constructor(address: string);
+ static isValid(address: string): boolean;
+ parse(address: string): string[];
+ /**
+ * Returns the correct form of an address
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ correctForm(): string;
+ /**
+ * Returns true if the address is correct, false otherwise
+ * @memberof Address4
+ * @instance
+ * @returns {Boolean}
+ */
+ isCorrect: (this: Address4 | import("./ipv6").Address6) => boolean;
+ /**
+ * Converts a hex string to an IPv4 address object
+ * @memberof Address4
+ * @static
+ * @param {string} hex - a hex string to convert
+ * @returns {Address4}
+ */
+ static fromHex(hex: string): Address4;
+ /**
+ * Converts an integer into a IPv4 address object
+ * @memberof Address4
+ * @static
+ * @param {integer} integer - a number to convert
+ * @returns {Address4}
+ */
+ static fromInteger(integer: number): Address4;
+ /**
+ * Return an address from in-addr.arpa form
+ * @memberof Address4
+ * @static
+ * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
+ * @returns {Adress4}
+ * @example
+ * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
+ * address.correctForm(); // '192.0.2.42'
+ */
+ static fromArpa(arpaFormAddress: string): Address4;
+ /**
+ * Converts an IPv4 address object to a hex string
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ toHex(): string;
+ /**
+ * Converts an IPv4 address object to an array of bytes
+ * @memberof Address4
+ * @instance
+ * @returns {Array}
+ */
+ toArray(): number[];
+ /**
+ * Converts an IPv4 address object to an IPv6 address group
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ toGroup6(): string;
+ /**
+ * Returns the address as a `bigint`
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ bigInt(): bigint;
+ /**
+ * Helper function getting start address.
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ _startAddress(): bigint;
+ /**
+ * The first address in the range given by this address' subnet.
+ * Often referred to as the Network Address.
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ startAddress(): Address4;
+ /**
+ * The first host address in the range given by this address's subnet ie
+ * the first address after the Network Address
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ startAddressExclusive(): Address4;
+ /**
+ * Helper function getting end address.
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ _endAddress(): bigint;
+ /**
+ * The last address in the range given by this address' subnet
+ * Often referred to as the Broadcast
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ endAddress(): Address4;
+ /**
+ * The last host address in the range given by this address's subnet ie
+ * the last address prior to the Broadcast Address
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ endAddressExclusive(): Address4;
+ /**
+ * Converts a BigInt to a v4 address object
+ * @memberof Address4
+ * @static
+ * @param {bigint} bigInt - a BigInt to convert
+ * @returns {Address4}
+ */
+ static fromBigInt(bigInt: bigint): Address4;
+ /**
+ * Convert a byte array to an Address4 object
+ * @memberof Address4
+ * @static
+ * @param {Array} bytes - an array of 4 bytes (0-255)
+ * @returns {Address4}
+ */
+ static fromByteArray(bytes: Array): Address4;
+ /**
+ * Convert an unsigned byte array to an Address4 object
+ * @memberof Address4
+ * @static
+ * @param {Array} bytes - an array of 4 unsigned bytes (0-255)
+ * @returns {Address4}
+ */
+ static fromUnsignedByteArray(bytes: Array): Address4;
+ /**
+ * Returns the first n bits of the address, defaulting to the
+ * subnet mask
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ mask(mask?: number): string;
+ /**
+ * Returns the bits in the given range as a base-2 string
+ * @memberof Address4
+ * @instance
+ * @returns {string}
+ */
+ getBitsBase2(start: number, end: number): string;
+ /**
+ * Return the reversed ip6.arpa form of the address
+ * @memberof Address4
+ * @param {Object} options
+ * @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
+ * @instance
+ * @returns {String}
+ */
+ reverseForm(options?: common.ReverseFormOptions): string;
+ /**
+ * Returns true if the given address is in the subnet of the current address
+ * @memberof Address4
+ * @instance
+ * @returns {boolean}
+ */
+ isInSubnet: typeof common.isInSubnet;
+ /**
+ * Returns true if the given address is a multicast address
+ * @memberof Address4
+ * @instance
+ * @returns {boolean}
+ */
+ isMulticast(): boolean;
+ /**
+ * Returns a zero-padded base-2 string representation of the address
+ * @memberof Address4
+ * @instance
+ * @returns {string}
+ */
+ binaryZeroPad(): string;
+ /**
+ * Groups an IPv4 address for inclusion at the end of an IPv6 address
+ * @returns {String}
+ */
+ groupForV6(): string;
+}
+//# sourceMappingURL=ipv4.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.d.ts.map
new file mode 100644
index 000000000..165d45912
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"ipv4.d.ts","sourceRoot":"","sources":["../src/ipv4.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAInC;;;;GAIG;AACH,qBAAa,QAAQ;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAoB;IAClC,aAAa,EAAE,MAAM,EAAE,CAAM;IAC7B,YAAY,EAAE,MAAM,CAAM;IAC1B,MAAM,EAAE,MAAM,CAAS;IACvB,UAAU,EAAE,MAAM,CAAM;IACxB,EAAE,EAAE,OAAO,CAAQ;gBAEP,OAAO,EAAE,MAAM;IAsB3B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAcxC,KAAK,CAAC,OAAO,EAAE,MAAM;IAUrB;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;OAKG;IACH,SAAS,0DAAoC;IAE7C;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;IAcrC;;;;;;OAMG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAI7C;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ;IASlD;;;;;OAKG;IACH,KAAK,IAAI,MAAM;IAIf;;;;;OAKG;IACH,OAAO,IAAI,MAAM,EAAE;IAInB;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;IAelB;;;;;OAKG;IACH,MAAM,IAAI,MAAM;IAIhB;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;;;OAMG;IACH,YAAY,IAAI,QAAQ;IAIxB;;;;;;OAMG;IACH,qBAAqB,IAAI,QAAQ;IAKjC;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;;OAMG;IACH,UAAU,IAAI,QAAQ;IAItB;;;;;;OAMG;IACH,mBAAmB,IAAI,QAAQ;IAK/B;;;;;;OAMG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAI3C;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,QAAQ;IAepD;;;;;;OAMG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,QAAQ;IAS5D;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAQ3B;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAIhD;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,kBAAkB,GAAG,MAAM;IAcxD;;;;;OAKG;IACH,UAAU,2BAAqB;IAE/B;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;OAGG;IACH,UAAU,IAAI,MAAM;CAYrB"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.js
new file mode 100644
index 000000000..311c89c69
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.js
@@ -0,0 +1,360 @@
+"use strict";
+/* eslint-disable no-param-reassign */
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Address4 = void 0;
+const common = __importStar(require("./common"));
+const constants = __importStar(require("./v4/constants"));
+const address_error_1 = require("./address-error");
+/**
+ * Represents an IPv4 address
+ * @class Address4
+ * @param {string} address - An IPv4 address string
+ */
+class Address4 {
+ constructor(address) {
+ this.groups = constants.GROUPS;
+ this.parsedAddress = [];
+ this.parsedSubnet = '';
+ this.subnet = '/32';
+ this.subnetMask = 32;
+ this.v4 = true;
+ /**
+ * Returns true if the address is correct, false otherwise
+ * @memberof Address4
+ * @instance
+ * @returns {Boolean}
+ */
+ this.isCorrect = common.isCorrect(constants.BITS);
+ /**
+ * Returns true if the given address is in the subnet of the current address
+ * @memberof Address4
+ * @instance
+ * @returns {boolean}
+ */
+ this.isInSubnet = common.isInSubnet;
+ this.address = address;
+ const subnet = constants.RE_SUBNET_STRING.exec(address);
+ if (subnet) {
+ this.parsedSubnet = subnet[0].replace('/', '');
+ this.subnetMask = parseInt(this.parsedSubnet, 10);
+ this.subnet = `/${this.subnetMask}`;
+ if (this.subnetMask < 0 || this.subnetMask > constants.BITS) {
+ throw new address_error_1.AddressError('Invalid subnet mask.');
+ }
+ address = address.replace(constants.RE_SUBNET_STRING, '');
+ }
+ this.addressMinusSuffix = address;
+ this.parsedAddress = this.parse(address);
+ }
+ static isValid(address) {
+ try {
+ // eslint-disable-next-line no-new
+ new Address4(address);
+ return true;
+ }
+ catch (e) {
+ return false;
+ }
+ }
+ /*
+ * Parses a v4 address
+ */
+ parse(address) {
+ const groups = address.split('.');
+ if (!address.match(constants.RE_ADDRESS)) {
+ throw new address_error_1.AddressError('Invalid IPv4 address.');
+ }
+ return groups;
+ }
+ /**
+ * Returns the correct form of an address
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ correctForm() {
+ return this.parsedAddress.map((part) => parseInt(part, 10)).join('.');
+ }
+ /**
+ * Converts a hex string to an IPv4 address object
+ * @memberof Address4
+ * @static
+ * @param {string} hex - a hex string to convert
+ * @returns {Address4}
+ */
+ static fromHex(hex) {
+ const padded = hex.replace(/:/g, '').padStart(8, '0');
+ const groups = [];
+ let i;
+ for (i = 0; i < 8; i += 2) {
+ const h = padded.slice(i, i + 2);
+ groups.push(parseInt(h, 16));
+ }
+ return new Address4(groups.join('.'));
+ }
+ /**
+ * Converts an integer into a IPv4 address object
+ * @memberof Address4
+ * @static
+ * @param {integer} integer - a number to convert
+ * @returns {Address4}
+ */
+ static fromInteger(integer) {
+ return Address4.fromHex(integer.toString(16));
+ }
+ /**
+ * Return an address from in-addr.arpa form
+ * @memberof Address4
+ * @static
+ * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
+ * @returns {Adress4}
+ * @example
+ * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
+ * address.correctForm(); // '192.0.2.42'
+ */
+ static fromArpa(arpaFormAddress) {
+ // remove ending ".in-addr.arpa." or just "."
+ const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, '');
+ const address = leader.split('.').reverse().join('.');
+ return new Address4(address);
+ }
+ /**
+ * Converts an IPv4 address object to a hex string
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ toHex() {
+ return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':');
+ }
+ /**
+ * Converts an IPv4 address object to an array of bytes
+ * @memberof Address4
+ * @instance
+ * @returns {Array}
+ */
+ toArray() {
+ return this.parsedAddress.map((part) => parseInt(part, 10));
+ }
+ /**
+ * Converts an IPv4 address object to an IPv6 address group
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ toGroup6() {
+ const output = [];
+ let i;
+ for (i = 0; i < constants.GROUPS; i += 2) {
+ output.push(`${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(this.parsedAddress[i + 1])}`);
+ }
+ return output.join(':');
+ }
+ /**
+ * Returns the address as a `bigint`
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ bigInt() {
+ return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join('')}`);
+ }
+ /**
+ * Helper function getting start address.
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ _startAddress() {
+ return BigInt(`0b${this.mask() + '0'.repeat(constants.BITS - this.subnetMask)}`);
+ }
+ /**
+ * The first address in the range given by this address' subnet.
+ * Often referred to as the Network Address.
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ startAddress() {
+ return Address4.fromBigInt(this._startAddress());
+ }
+ /**
+ * The first host address in the range given by this address's subnet ie
+ * the first address after the Network Address
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ startAddressExclusive() {
+ const adjust = BigInt('1');
+ return Address4.fromBigInt(this._startAddress() + adjust);
+ }
+ /**
+ * Helper function getting end address.
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ _endAddress() {
+ return BigInt(`0b${this.mask() + '1'.repeat(constants.BITS - this.subnetMask)}`);
+ }
+ /**
+ * The last address in the range given by this address' subnet
+ * Often referred to as the Broadcast
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ endAddress() {
+ return Address4.fromBigInt(this._endAddress());
+ }
+ /**
+ * The last host address in the range given by this address's subnet ie
+ * the last address prior to the Broadcast Address
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ endAddressExclusive() {
+ const adjust = BigInt('1');
+ return Address4.fromBigInt(this._endAddress() - adjust);
+ }
+ /**
+ * Converts a BigInt to a v4 address object
+ * @memberof Address4
+ * @static
+ * @param {bigint} bigInt - a BigInt to convert
+ * @returns {Address4}
+ */
+ static fromBigInt(bigInt) {
+ return Address4.fromHex(bigInt.toString(16));
+ }
+ /**
+ * Convert a byte array to an Address4 object
+ * @memberof Address4
+ * @static
+ * @param {Array} bytes - an array of 4 bytes (0-255)
+ * @returns {Address4}
+ */
+ static fromByteArray(bytes) {
+ if (bytes.length !== 4) {
+ throw new address_error_1.AddressError('IPv4 addresses require exactly 4 bytes');
+ }
+ // Validate that all bytes are within valid range (0-255)
+ for (let i = 0; i < bytes.length; i++) {
+ if (!Number.isInteger(bytes[i]) || bytes[i] < 0 || bytes[i] > 255) {
+ throw new address_error_1.AddressError('All bytes must be integers between 0 and 255');
+ }
+ }
+ return this.fromUnsignedByteArray(bytes);
+ }
+ /**
+ * Convert an unsigned byte array to an Address4 object
+ * @memberof Address4
+ * @static
+ * @param {Array} bytes - an array of 4 unsigned bytes (0-255)
+ * @returns {Address4}
+ */
+ static fromUnsignedByteArray(bytes) {
+ if (bytes.length !== 4) {
+ throw new address_error_1.AddressError('IPv4 addresses require exactly 4 bytes');
+ }
+ const address = bytes.join('.');
+ return new Address4(address);
+ }
+ /**
+ * Returns the first n bits of the address, defaulting to the
+ * subnet mask
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ mask(mask) {
+ if (mask === undefined) {
+ mask = this.subnetMask;
+ }
+ return this.getBitsBase2(0, mask);
+ }
+ /**
+ * Returns the bits in the given range as a base-2 string
+ * @memberof Address4
+ * @instance
+ * @returns {string}
+ */
+ getBitsBase2(start, end) {
+ return this.binaryZeroPad().slice(start, end);
+ }
+ /**
+ * Return the reversed ip6.arpa form of the address
+ * @memberof Address4
+ * @param {Object} options
+ * @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
+ * @instance
+ * @returns {String}
+ */
+ reverseForm(options) {
+ if (!options) {
+ options = {};
+ }
+ const reversed = this.correctForm().split('.').reverse().join('.');
+ if (options.omitSuffix) {
+ return reversed;
+ }
+ return `${reversed}.in-addr.arpa.`;
+ }
+ /**
+ * Returns true if the given address is a multicast address
+ * @memberof Address4
+ * @instance
+ * @returns {boolean}
+ */
+ isMulticast() {
+ return this.isInSubnet(new Address4('224.0.0.0/4'));
+ }
+ /**
+ * Returns a zero-padded base-2 string representation of the address
+ * @memberof Address4
+ * @instance
+ * @returns {string}
+ */
+ binaryZeroPad() {
+ return this.bigInt().toString(2).padStart(constants.BITS, '0');
+ }
+ /**
+ * Groups an IPv4 address for inclusion at the end of an IPv6 address
+ * @returns {String}
+ */
+ groupForV6() {
+ const segments = this.parsedAddress;
+ return this.address.replace(constants.RE_ADDRESS, `${segments
+ .slice(0, 2)
+ .join('.')} .${segments
+ .slice(2, 4)
+ .join('.')} `);
+ }
+}
+exports.Address4 = Address4;
+//# sourceMappingURL=ipv4.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.js.map
new file mode 100644
index 000000000..2b4e92804
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv4.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"ipv4.js","sourceRoot":"","sources":["../src/ipv4.ts"],"names":[],"mappings":";AAAA,sCAAsC;;;;;;;;;;;;;;;;;;;;;;;;;;AAEtC,iDAAmC;AACnC,0DAA4C;AAC5C,mDAA+C;AAE/C;;;;GAIG;AACH,MAAa,QAAQ;IAUnB,YAAY,OAAe;QAP3B,WAAM,GAAW,SAAS,CAAC,MAAM,CAAC;QAClC,kBAAa,GAAa,EAAE,CAAC;QAC7B,iBAAY,GAAW,EAAE,CAAC;QAC1B,WAAM,GAAW,KAAK,CAAC;QACvB,eAAU,GAAW,EAAE,CAAC;QACxB,OAAE,GAAY,IAAI,CAAC;QA0DnB;;;;;WAKG;QACH,cAAS,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QA0Q7C;;;;;WAKG;QACH,eAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QA7U7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAEvB,MAAM,MAAM,GAAG,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAExD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC/C,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAEpC,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC5D,MAAM,IAAI,4BAAY,CAAC,sBAAsB,CAAC,CAAC;YACjD,CAAC;YAED,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;QAElC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAAe;QAC5B,IAAI,CAAC;YACH,kCAAkC;YAClC,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;YAEtB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe;QACnB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,4BAAY,CAAC,uBAAuB,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxE,CAAC;IAUD;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,GAAW;QACxB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,CAAC;QAEN,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YAEjC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/B,CAAC;QAED,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,WAAW,CAAC,OAAe;QAChC,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,eAAuB;QACrC,6CAA6C;QAC7C,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;QAEpE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEtD,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,KAAK;QACH,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpF,CAAC;IAED;;;;;OAKG;IACH,OAAO;QACL,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACN,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,CAAC;QAEN,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CACT,GAAG,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,iBAAiB,CAC3E,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAC1B,EAAE,CACJ,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED;;;;;OAKG;IACH,aAAa;QACX,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IAED;;;;;;OAMG;IACH,YAAY;QACV,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;OAMG;IACH,qBAAqB;QACnB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,MAAM,CAAC,CAAC;IAC5D,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IAED;;;;;;OAMG;IACH,UAAU;QACR,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;OAMG;IACH,mBAAmB;QACjB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,CAAC;IAC1D,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,UAAU,CAAC,MAAc;QAC9B,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAAC,KAAoB;QACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,4BAAY,CAAC,wCAAwC,CAAC,CAAC;QACnE,CAAC;QAED,yDAAyD;QACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC;gBAClE,MAAM,IAAI,4BAAY,CAAC,8CAA8C,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAoB;QAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,4BAAY,CAAC,wCAAwC,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,IAAa;QAChB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QAED,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,KAAa,EAAE,GAAW;QACrC,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChD,CAAC;IAED;;;;;;;OAOG;IACH,WAAW,CAAC,OAAmC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,EAAE,CAAC;QACf,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEnE,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,OAAO,GAAG,QAAQ,gBAAgB,CAAC;IACrC,CAAC;IAUD;;;;;OAKG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;IACtD,CAAC;IAED;;;;;OAKG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC;QAEpC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CACzB,SAAS,CAAC,UAAU,EACpB,8CAA8C,QAAQ;aACnD,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,IAAI,CAAC,GAAG,CAAC,sDAAsD,QAAQ;aACvE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,IAAI,CAAC,GAAG,CAAC,SAAS,CACtB,CAAC;IACJ,CAAC;CACF;AA9XD,4BA8XC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.d.ts
new file mode 100644
index 000000000..2a7c7e0b3
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.d.ts
@@ -0,0 +1,428 @@
+import * as common from './common';
+import { Address4 } from './ipv4';
+interface SixToFourProperties {
+ prefix: string;
+ gateway: string;
+}
+interface TeredoProperties {
+ prefix: string;
+ server4: string;
+ client4: string;
+ flags: string;
+ coneNat: boolean;
+ microsoft: {
+ reserved: boolean;
+ universalLocal: boolean;
+ groupIndividual: boolean;
+ nonce: string;
+ };
+ udpPort: string;
+}
+/**
+ * Represents an IPv6 address
+ * @class Address6
+ * @param {string} address - An IPv6 address string
+ * @param {number} [groups=8] - How many octets to parse
+ * @example
+ * var address = new Address6('2001::/32');
+ */
+export declare class Address6 {
+ address4?: Address4;
+ address: string;
+ addressMinusSuffix: string;
+ elidedGroups?: number;
+ elisionBegin?: number;
+ elisionEnd?: number;
+ groups: number;
+ parsedAddress4?: string;
+ parsedAddress: string[];
+ parsedSubnet: string;
+ subnet: string;
+ subnetMask: number;
+ v4: boolean;
+ zone: string;
+ constructor(address: string, optionalGroups?: number);
+ static isValid(address: string): boolean;
+ /**
+ * Convert a BigInt to a v6 address object
+ * @memberof Address6
+ * @static
+ * @param {bigint} bigInt - a BigInt to convert
+ * @returns {Address6}
+ * @example
+ * var bigInt = BigInt('1000000000000');
+ * var address = Address6.fromBigInt(bigInt);
+ * address.correctForm(); // '::e8:d4a5:1000'
+ */
+ static fromBigInt(bigInt: bigint): Address6;
+ /**
+ * Convert a URL (with optional port number) to an address object
+ * @memberof Address6
+ * @static
+ * @param {string} url - a URL with optional port number
+ * @example
+ * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');
+ * addressAndPort.address.correctForm(); // 'ffff::'
+ * addressAndPort.port; // 8080
+ */
+ static fromURL(url: string): {
+ error: string;
+ address: null;
+ port: null;
+ } | {
+ address: Address6;
+ port: number | null;
+ error?: undefined;
+ };
+ /**
+ * Create an IPv6-mapped address given an IPv4 address
+ * @memberof Address6
+ * @static
+ * @param {string} address - An IPv4 address string
+ * @returns {Address6}
+ * @example
+ * var address = Address6.fromAddress4('192.168.0.1');
+ * address.correctForm(); // '::ffff:c0a8:1'
+ * address.to4in6(); // '::ffff:192.168.0.1'
+ */
+ static fromAddress4(address: string): Address6;
+ /**
+ * Return an address from ip6.arpa form
+ * @memberof Address6
+ * @static
+ * @param {string} arpaFormAddress - an 'ip6.arpa' form address
+ * @returns {Adress6}
+ * @example
+ * var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)
+ * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'
+ */
+ static fromArpa(arpaFormAddress: string): Address6;
+ /**
+ * Return the Microsoft UNC transcription of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String} the Microsoft UNC transcription of the address
+ */
+ microsoftTranscription(): string;
+ /**
+ * Return the first n bits of the address, defaulting to the subnet mask
+ * @memberof Address6
+ * @instance
+ * @param {number} [mask=subnet] - the number of bits to mask
+ * @returns {String} the first n bits of the address as a string
+ */
+ mask(mask?: number): string;
+ /**
+ * Return the number of possible subnets of a given size in the address
+ * @memberof Address6
+ * @instance
+ * @param {number} [subnetSize=128] - the subnet size
+ * @returns {String}
+ */
+ possibleSubnets(subnetSize?: number): string;
+ /**
+ * Helper function getting start address.
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ _startAddress(): bigint;
+ /**
+ * The first address in the range given by this address' subnet
+ * Often referred to as the Network Address.
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ startAddress(): Address6;
+ /**
+ * The first host address in the range given by this address's subnet ie
+ * the first address after the Network Address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ startAddressExclusive(): Address6;
+ /**
+ * Helper function getting end address.
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ _endAddress(): bigint;
+ /**
+ * The last address in the range given by this address' subnet
+ * Often referred to as the Broadcast
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ endAddress(): Address6;
+ /**
+ * The last host address in the range given by this address's subnet ie
+ * the last address prior to the Broadcast Address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ endAddressExclusive(): Address6;
+ /**
+ * Return the scope of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getScope(): string;
+ /**
+ * Return the type of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getType(): string;
+ /**
+ * Return the bits in the given range as a BigInt
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ getBits(start: number, end: number): bigint;
+ /**
+ * Return the bits in the given range as a base-2 string
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsBase2(start: number, end: number): string;
+ /**
+ * Return the bits in the given range as a base-16 string
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsBase16(start: number, end: number): string;
+ /**
+ * Return the bits that are set past the subnet mask length
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsPastSubnet(): string;
+ /**
+ * Return the reversed ip6.arpa form of the address
+ * @memberof Address6
+ * @param {Object} options
+ * @param {boolean} options.omitSuffix - omit the "ip6.arpa" suffix
+ * @instance
+ * @returns {String}
+ */
+ reverseForm(options?: common.ReverseFormOptions): string;
+ /**
+ * Return the correct form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ correctForm(): string;
+ /**
+ * Return a zero-padded base-2 string representation of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ * @example
+ * var address = new Address6('2001:4860:4001:803::1011');
+ * address.binaryZeroPad();
+ * // '0010000000000001010010000110000001000000000000010000100000000011
+ * // 0000000000000000000000000000000000000000000000000001000000010001'
+ */
+ binaryZeroPad(): string;
+ parse4in6(address: string): string;
+ parse(address: string): string[];
+ /**
+ * Return the canonical form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ canonicalForm(): string;
+ /**
+ * Return the decimal form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ decimal(): string;
+ /**
+ * Return the address as a BigInt
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ bigInt(): bigint;
+ /**
+ * Return the last two groups of this address as an IPv4 address string
+ * @memberof Address6
+ * @instance
+ * @returns {Address4}
+ * @example
+ * var address = new Address6('2001:4860:4001::1825:bf11');
+ * address.to4().correctForm(); // '24.37.191.17'
+ */
+ to4(): Address4;
+ /**
+ * Return the v4-in-v6 form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ to4in6(): string;
+ /**
+ * Return an object containing the Teredo properties of the address
+ * @memberof Address6
+ * @instance
+ * @returns {Object}
+ */
+ inspectTeredo(): TeredoProperties;
+ /**
+ * Return an object containing the 6to4 properties of the address
+ * @memberof Address6
+ * @instance
+ * @returns {Object}
+ */
+ inspect6to4(): SixToFourProperties;
+ /**
+ * Return a v6 6to4 address from a v6 v4inv6 address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ to6to4(): Address6 | null;
+ /**
+ * Return a byte array
+ * @memberof Address6
+ * @instance
+ * @returns {Array}
+ */
+ toByteArray(): number[];
+ /**
+ * Return an unsigned byte array
+ * @memberof Address6
+ * @instance
+ * @returns {Array}
+ */
+ toUnsignedByteArray(): number[];
+ /**
+ * Convert a byte array to an Address6 object
+ * @memberof Address6
+ * @static
+ * @returns {Address6}
+ */
+ static fromByteArray(bytes: Array): Address6;
+ /**
+ * Convert an unsigned byte array to an Address6 object
+ * @memberof Address6
+ * @static
+ * @returns {Address6}
+ */
+ static fromUnsignedByteArray(bytes: Array): Address6;
+ /**
+ * Returns true if the given address is in the subnet of the current address
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isInSubnet: typeof common.isInSubnet;
+ /**
+ * Returns true if the address is correct, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isCorrect: (this: Address4 | Address6) => boolean;
+ /**
+ * Returns true if the address is in the canonical form, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isCanonical(): boolean;
+ /**
+ * Returns true if the address is a link local address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isLinkLocal(): boolean;
+ /**
+ * Returns true if the address is a multicast address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isMulticast(): boolean;
+ /**
+ * Returns true if the address is a v4-in-v6 address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ is4(): boolean;
+ /**
+ * Returns true if the address is a Teredo address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isTeredo(): boolean;
+ /**
+ * Returns true if the address is a 6to4 address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ is6to4(): boolean;
+ /**
+ * Returns true if the address is a loopback address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isLoopback(): boolean;
+ /**
+ * @returns {String} the address in link form with a default port of 80
+ */
+ href(optionalPort?: number | string): string;
+ /**
+ * @returns {String} a link suitable for conveying the address via a URL hash
+ */
+ link(options?: {
+ className?: string;
+ prefix?: string;
+ v4?: boolean;
+ }): string;
+ /**
+ * Groups an address
+ * @returns {String}
+ */
+ group(): string;
+ /**
+ * Generate a regular expression string that can be used to find or validate
+ * all variations of this address
+ * @memberof Address6
+ * @instance
+ * @param {boolean} substringSearch
+ * @returns {string}
+ */
+ regularExpressionString(this: Address6, substringSearch?: boolean): string;
+ /**
+ * Generate a regular expression that can be used to find or validate all
+ * variations of this address.
+ * @memberof Address6
+ * @instance
+ * @param {boolean} substringSearch
+ * @returns {RegExp}
+ */
+ regularExpression(this: Address6, substringSearch?: boolean): RegExp;
+}
+export {};
+//# sourceMappingURL=ipv6.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.d.ts.map
new file mode 100644
index 000000000..37ead6aa0
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"ipv6.d.ts","sourceRoot":"","sources":["../src/ipv6.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAInC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AA4DlC,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE;QACT,QAAQ,EAAE,OAAO,CAAC;QAClB,cAAc,EAAE,OAAO,CAAC;QACxB,eAAe,EAAE,OAAO,CAAC;QACzB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,qBAAa,QAAQ;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAM;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAM;IAC1B,MAAM,EAAE,MAAM,CAAU;IACxB,UAAU,EAAE,MAAM,CAAO;IACzB,EAAE,EAAE,OAAO,CAAS;IACpB,IAAI,EAAE,MAAM,CAAM;gBAEN,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM;IA0CpD,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAWxC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAY3C;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;;;;;;;;;IA4D1B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAQ9C;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ;IAsBlD;;;;;OAKG;IACH,sBAAsB,IAAI,MAAM;IAIhC;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM;IAI5C;;;;;;OAMG;IAEH,eAAe,CAAC,UAAU,GAAE,MAAY,GAAG,MAAM;IAYjD;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;;;OAMG;IACH,YAAY,IAAI,QAAQ;IAIxB;;;;;;OAMG;IACH,qBAAqB,IAAI,QAAQ;IAKjC;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;;OAMG;IACH,UAAU,IAAI,QAAQ;IAItB;;;;;;OAMG;IACH,mBAAmB,IAAI,QAAQ;IAK/B;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;IAUlB;;;;;OAKG;IACH,OAAO,IAAI,MAAM;IAUjB;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAI3C;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAIhD;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAYjD;;;;;OAKG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,kBAAkB,GAAG,MAAM;IA6BxD;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAqDrB;;;;;;;;;;OAUG;IACH,aAAa,IAAI,MAAM;IAKvB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAiClC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IA0EhC;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;;OAKG;IACH,OAAO,IAAI,MAAM;IAIjB;;;;;OAKG;IACH,MAAM,IAAI,MAAM;IAIhB;;;;;;;;OAQG;IACH,GAAG,IAAI,QAAQ;IAMf;;;;;OAKG;IACH,MAAM,IAAI,MAAM;IAehB;;;;;OAKG;IACH,aAAa,IAAI,gBAAgB;IA0DjC;;;;;OAKG;IACH,WAAW,IAAI,mBAAmB;IAgBlC;;;;;OAKG;IACH,MAAM,IAAI,QAAQ,GAAG,IAAI;IAgBzB;;;;;OAKG;IACH,WAAW,IAAI,MAAM,EAAE;IAcvB;;;;;OAKG;IACH,mBAAmB,IAAI,MAAM,EAAE;IAI/B;;;;;OAKG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,QAAQ;IAIjD;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,QAAQ;IAezD;;;;;OAKG;IACH,UAAU,2BAAqB;IAE/B;;;;;OAKG;IACH,SAAS,yCAAqC;IAE9C;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAYtB;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;OAKG;IACH,GAAG,IAAI,OAAO;IAId;;;;;OAKG;IACH,QAAQ,IAAI,OAAO;IAInB;;;;;OAKG;IACH,MAAM,IAAI,OAAO;IAIjB;;;;;OAKG;IACH,UAAU,IAAI,OAAO;IAMrB;;OAEG;IACH,IAAI,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAU5C;;OAEG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;IAgC7E;;;OAGG;IACH,KAAK,IAAI,MAAM;IA8Cf;;;;;;;OAOG;IACH,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,GAAE,OAAe,GAAG,MAAM;IAgDjF;;;;;;;OAOG;IACH,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,GAAE,OAAe,GAAG,MAAM;CAI5E"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.js
new file mode 100644
index 000000000..5f88ab63a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.js
@@ -0,0 +1,1003 @@
+"use strict";
+/* eslint-disable prefer-destructuring */
+/* eslint-disable no-param-reassign */
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Address6 = void 0;
+const common = __importStar(require("./common"));
+const constants4 = __importStar(require("./v4/constants"));
+const constants6 = __importStar(require("./v6/constants"));
+const helpers = __importStar(require("./v6/helpers"));
+const ipv4_1 = require("./ipv4");
+const regular_expressions_1 = require("./v6/regular-expressions");
+const address_error_1 = require("./address-error");
+const common_1 = require("./common");
+function assert(condition) {
+ if (!condition) {
+ throw new Error('Assertion failed.');
+ }
+}
+function addCommas(number) {
+ const r = /(\d+)(\d{3})/;
+ while (r.test(number)) {
+ number = number.replace(r, '$1,$2');
+ }
+ return number;
+}
+function spanLeadingZeroes4(n) {
+ n = n.replace(/^(0{1,})([1-9]+)$/, '$1 $2');
+ n = n.replace(/^(0{1,})(0)$/, '$1 $2');
+ return n;
+}
+/*
+ * A helper function to compact an array
+ */
+function compact(address, slice) {
+ const s1 = [];
+ const s2 = [];
+ let i;
+ for (i = 0; i < address.length; i++) {
+ if (i < slice[0]) {
+ s1.push(address[i]);
+ }
+ else if (i > slice[1]) {
+ s2.push(address[i]);
+ }
+ }
+ return s1.concat(['compact']).concat(s2);
+}
+function paddedHex(octet) {
+ return parseInt(octet, 16).toString(16).padStart(4, '0');
+}
+function unsignByte(b) {
+ // eslint-disable-next-line no-bitwise
+ return b & 0xff;
+}
+/**
+ * Represents an IPv6 address
+ * @class Address6
+ * @param {string} address - An IPv6 address string
+ * @param {number} [groups=8] - How many octets to parse
+ * @example
+ * var address = new Address6('2001::/32');
+ */
+class Address6 {
+ constructor(address, optionalGroups) {
+ this.addressMinusSuffix = '';
+ this.parsedSubnet = '';
+ this.subnet = '/128';
+ this.subnetMask = 128;
+ this.v4 = false;
+ this.zone = '';
+ // #region Attributes
+ /**
+ * Returns true if the given address is in the subnet of the current address
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ this.isInSubnet = common.isInSubnet;
+ /**
+ * Returns true if the address is correct, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ this.isCorrect = common.isCorrect(constants6.BITS);
+ if (optionalGroups === undefined) {
+ this.groups = constants6.GROUPS;
+ }
+ else {
+ this.groups = optionalGroups;
+ }
+ this.address = address;
+ const subnet = constants6.RE_SUBNET_STRING.exec(address);
+ if (subnet) {
+ this.parsedSubnet = subnet[0].replace('/', '');
+ this.subnetMask = parseInt(this.parsedSubnet, 10);
+ this.subnet = `/${this.subnetMask}`;
+ if (Number.isNaN(this.subnetMask) ||
+ this.subnetMask < 0 ||
+ this.subnetMask > constants6.BITS) {
+ throw new address_error_1.AddressError('Invalid subnet mask.');
+ }
+ address = address.replace(constants6.RE_SUBNET_STRING, '');
+ }
+ else if (/\//.test(address)) {
+ throw new address_error_1.AddressError('Invalid subnet mask.');
+ }
+ const zone = constants6.RE_ZONE_STRING.exec(address);
+ if (zone) {
+ this.zone = zone[0];
+ address = address.replace(constants6.RE_ZONE_STRING, '');
+ }
+ this.addressMinusSuffix = address;
+ this.parsedAddress = this.parse(this.addressMinusSuffix);
+ }
+ static isValid(address) {
+ try {
+ // eslint-disable-next-line no-new
+ new Address6(address);
+ return true;
+ }
+ catch (e) {
+ return false;
+ }
+ }
+ /**
+ * Convert a BigInt to a v6 address object
+ * @memberof Address6
+ * @static
+ * @param {bigint} bigInt - a BigInt to convert
+ * @returns {Address6}
+ * @example
+ * var bigInt = BigInt('1000000000000');
+ * var address = Address6.fromBigInt(bigInt);
+ * address.correctForm(); // '::e8:d4a5:1000'
+ */
+ static fromBigInt(bigInt) {
+ const hex = bigInt.toString(16).padStart(32, '0');
+ const groups = [];
+ let i;
+ for (i = 0; i < constants6.GROUPS; i++) {
+ groups.push(hex.slice(i * 4, (i + 1) * 4));
+ }
+ return new Address6(groups.join(':'));
+ }
+ /**
+ * Convert a URL (with optional port number) to an address object
+ * @memberof Address6
+ * @static
+ * @param {string} url - a URL with optional port number
+ * @example
+ * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');
+ * addressAndPort.address.correctForm(); // 'ffff::'
+ * addressAndPort.port; // 8080
+ */
+ static fromURL(url) {
+ let host;
+ let port = null;
+ let result;
+ // If we have brackets parse them and find a port
+ if (url.indexOf('[') !== -1 && url.indexOf(']:') !== -1) {
+ result = constants6.RE_URL_WITH_PORT.exec(url);
+ if (result === null) {
+ return {
+ error: 'failed to parse address with port',
+ address: null,
+ port: null,
+ };
+ }
+ host = result[1];
+ port = result[2];
+ // If there's a URL extract the address
+ }
+ else if (url.indexOf('/') !== -1) {
+ // Remove the protocol prefix
+ url = url.replace(/^[a-z0-9]+:\/\//, '');
+ // Parse the address
+ result = constants6.RE_URL.exec(url);
+ if (result === null) {
+ return {
+ error: 'failed to parse address from URL',
+ address: null,
+ port: null,
+ };
+ }
+ host = result[1];
+ // Otherwise just assign the URL to the host and let the library parse it
+ }
+ else {
+ host = url;
+ }
+ // If there's a port convert it to an integer
+ if (port) {
+ port = parseInt(port, 10);
+ // squelch out of range ports
+ if (port < 0 || port > 65536) {
+ port = null;
+ }
+ }
+ else {
+ // Standardize `undefined` to `null`
+ port = null;
+ }
+ return {
+ address: new Address6(host),
+ port,
+ };
+ }
+ /**
+ * Create an IPv6-mapped address given an IPv4 address
+ * @memberof Address6
+ * @static
+ * @param {string} address - An IPv4 address string
+ * @returns {Address6}
+ * @example
+ * var address = Address6.fromAddress4('192.168.0.1');
+ * address.correctForm(); // '::ffff:c0a8:1'
+ * address.to4in6(); // '::ffff:192.168.0.1'
+ */
+ static fromAddress4(address) {
+ const address4 = new ipv4_1.Address4(address);
+ const mask6 = constants6.BITS - (constants4.BITS - address4.subnetMask);
+ return new Address6(`::ffff:${address4.correctForm()}/${mask6}`);
+ }
+ /**
+ * Return an address from ip6.arpa form
+ * @memberof Address6
+ * @static
+ * @param {string} arpaFormAddress - an 'ip6.arpa' form address
+ * @returns {Adress6}
+ * @example
+ * var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)
+ * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'
+ */
+ static fromArpa(arpaFormAddress) {
+ // remove ending ".ip6.arpa." or just "."
+ let address = arpaFormAddress.replace(/(\.ip6\.arpa)?\.$/, '');
+ const semicolonAmount = 7;
+ // correct ip6.arpa form with ending removed will be 63 characters
+ if (address.length !== 63) {
+ throw new address_error_1.AddressError("Invalid 'ip6.arpa' form.");
+ }
+ const parts = address.split('.').reverse();
+ for (let i = semicolonAmount; i > 0; i--) {
+ const insertIndex = i * 4;
+ parts.splice(insertIndex, 0, ':');
+ }
+ address = parts.join('');
+ return new Address6(address);
+ }
+ /**
+ * Return the Microsoft UNC transcription of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String} the Microsoft UNC transcription of the address
+ */
+ microsoftTranscription() {
+ return `${this.correctForm().replace(/:/g, '-')}.ipv6-literal.net`;
+ }
+ /**
+ * Return the first n bits of the address, defaulting to the subnet mask
+ * @memberof Address6
+ * @instance
+ * @param {number} [mask=subnet] - the number of bits to mask
+ * @returns {String} the first n bits of the address as a string
+ */
+ mask(mask = this.subnetMask) {
+ return this.getBitsBase2(0, mask);
+ }
+ /**
+ * Return the number of possible subnets of a given size in the address
+ * @memberof Address6
+ * @instance
+ * @param {number} [subnetSize=128] - the subnet size
+ * @returns {String}
+ */
+ // TODO: probably useful to have a numeric version of this too
+ possibleSubnets(subnetSize = 128) {
+ const availableBits = constants6.BITS - this.subnetMask;
+ const subnetBits = Math.abs(subnetSize - constants6.BITS);
+ const subnetPowers = availableBits - subnetBits;
+ if (subnetPowers < 0) {
+ return '0';
+ }
+ return addCommas((BigInt('2') ** BigInt(subnetPowers)).toString(10));
+ }
+ /**
+ * Helper function getting start address.
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ _startAddress() {
+ return BigInt(`0b${this.mask() + '0'.repeat(constants6.BITS - this.subnetMask)}`);
+ }
+ /**
+ * The first address in the range given by this address' subnet
+ * Often referred to as the Network Address.
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ startAddress() {
+ return Address6.fromBigInt(this._startAddress());
+ }
+ /**
+ * The first host address in the range given by this address's subnet ie
+ * the first address after the Network Address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ startAddressExclusive() {
+ const adjust = BigInt('1');
+ return Address6.fromBigInt(this._startAddress() + adjust);
+ }
+ /**
+ * Helper function getting end address.
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ _endAddress() {
+ return BigInt(`0b${this.mask() + '1'.repeat(constants6.BITS - this.subnetMask)}`);
+ }
+ /**
+ * The last address in the range given by this address' subnet
+ * Often referred to as the Broadcast
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ endAddress() {
+ return Address6.fromBigInt(this._endAddress());
+ }
+ /**
+ * The last host address in the range given by this address's subnet ie
+ * the last address prior to the Broadcast Address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ endAddressExclusive() {
+ const adjust = BigInt('1');
+ return Address6.fromBigInt(this._endAddress() - adjust);
+ }
+ /**
+ * Return the scope of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getScope() {
+ let scope = constants6.SCOPES[parseInt(this.getBits(12, 16).toString(10), 10)];
+ if (this.getType() === 'Global unicast' && scope !== 'Link local') {
+ scope = 'Global';
+ }
+ return scope || 'Unknown';
+ }
+ /**
+ * Return the type of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getType() {
+ for (const subnet of Object.keys(constants6.TYPES)) {
+ if (this.isInSubnet(new Address6(subnet))) {
+ return constants6.TYPES[subnet];
+ }
+ }
+ return 'Global unicast';
+ }
+ /**
+ * Return the bits in the given range as a BigInt
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ getBits(start, end) {
+ return BigInt(`0b${this.getBitsBase2(start, end)}`);
+ }
+ /**
+ * Return the bits in the given range as a base-2 string
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsBase2(start, end) {
+ return this.binaryZeroPad().slice(start, end);
+ }
+ /**
+ * Return the bits in the given range as a base-16 string
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsBase16(start, end) {
+ const length = end - start;
+ if (length % 4 !== 0) {
+ throw new Error('Length of bits to retrieve must be divisible by four');
+ }
+ return this.getBits(start, end)
+ .toString(16)
+ .padStart(length / 4, '0');
+ }
+ /**
+ * Return the bits that are set past the subnet mask length
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsPastSubnet() {
+ return this.getBitsBase2(this.subnetMask, constants6.BITS);
+ }
+ /**
+ * Return the reversed ip6.arpa form of the address
+ * @memberof Address6
+ * @param {Object} options
+ * @param {boolean} options.omitSuffix - omit the "ip6.arpa" suffix
+ * @instance
+ * @returns {String}
+ */
+ reverseForm(options) {
+ if (!options) {
+ options = {};
+ }
+ const characters = Math.floor(this.subnetMask / 4);
+ const reversed = this.canonicalForm()
+ .replace(/:/g, '')
+ .split('')
+ .slice(0, characters)
+ .reverse()
+ .join('.');
+ if (characters > 0) {
+ if (options.omitSuffix) {
+ return reversed;
+ }
+ return `${reversed}.ip6.arpa.`;
+ }
+ if (options.omitSuffix) {
+ return '';
+ }
+ return 'ip6.arpa.';
+ }
+ /**
+ * Return the correct form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ correctForm() {
+ let i;
+ let groups = [];
+ let zeroCounter = 0;
+ const zeroes = [];
+ for (i = 0; i < this.parsedAddress.length; i++) {
+ const value = parseInt(this.parsedAddress[i], 16);
+ if (value === 0) {
+ zeroCounter++;
+ }
+ if (value !== 0 && zeroCounter > 0) {
+ if (zeroCounter > 1) {
+ zeroes.push([i - zeroCounter, i - 1]);
+ }
+ zeroCounter = 0;
+ }
+ }
+ // Do we end with a string of zeroes?
+ if (zeroCounter > 1) {
+ zeroes.push([this.parsedAddress.length - zeroCounter, this.parsedAddress.length - 1]);
+ }
+ const zeroLengths = zeroes.map((n) => n[1] - n[0] + 1);
+ if (zeroes.length > 0) {
+ const index = zeroLengths.indexOf(Math.max(...zeroLengths));
+ groups = compact(this.parsedAddress, zeroes[index]);
+ }
+ else {
+ groups = this.parsedAddress;
+ }
+ for (i = 0; i < groups.length; i++) {
+ if (groups[i] !== 'compact') {
+ groups[i] = parseInt(groups[i], 16).toString(16);
+ }
+ }
+ let correct = groups.join(':');
+ correct = correct.replace(/^compact$/, '::');
+ correct = correct.replace(/(^compact)|(compact$)/, ':');
+ correct = correct.replace(/compact/, '');
+ return correct;
+ }
+ /**
+ * Return a zero-padded base-2 string representation of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ * @example
+ * var address = new Address6('2001:4860:4001:803::1011');
+ * address.binaryZeroPad();
+ * // '0010000000000001010010000110000001000000000000010000100000000011
+ * // 0000000000000000000000000000000000000000000000000001000000010001'
+ */
+ binaryZeroPad() {
+ return this.bigInt().toString(2).padStart(constants6.BITS, '0');
+ }
+ // TODO: Improve the semantics of this helper function
+ parse4in6(address) {
+ const groups = address.split(':');
+ const lastGroup = groups.slice(-1)[0];
+ const address4 = lastGroup.match(constants4.RE_ADDRESS);
+ if (address4) {
+ this.parsedAddress4 = address4[0];
+ this.address4 = new ipv4_1.Address4(this.parsedAddress4);
+ for (let i = 0; i < this.address4.groups; i++) {
+ if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) {
+ throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.", address.replace(constants4.RE_ADDRESS, this.address4.parsedAddress.map(spanLeadingZeroes4).join('.')));
+ }
+ }
+ this.v4 = true;
+ groups[groups.length - 1] = this.address4.toGroup6();
+ address = groups.join(':');
+ }
+ return address;
+ }
+ // TODO: Make private?
+ parse(address) {
+ address = this.parse4in6(address);
+ const badCharacters = address.match(constants6.RE_BAD_CHARACTERS);
+ if (badCharacters) {
+ throw new address_error_1.AddressError(`Bad character${badCharacters.length > 1 ? 's' : ''} detected in address: ${badCharacters.join('')}`, address.replace(constants6.RE_BAD_CHARACTERS, '$1 '));
+ }
+ const badAddress = address.match(constants6.RE_BAD_ADDRESS);
+ if (badAddress) {
+ throw new address_error_1.AddressError(`Address failed regex: ${badAddress.join('')}`, address.replace(constants6.RE_BAD_ADDRESS, '$1 '));
+ }
+ let groups = [];
+ const halves = address.split('::');
+ if (halves.length === 2) {
+ let first = halves[0].split(':');
+ let last = halves[1].split(':');
+ if (first.length === 1 && first[0] === '') {
+ first = [];
+ }
+ if (last.length === 1 && last[0] === '') {
+ last = [];
+ }
+ const remaining = this.groups - (first.length + last.length);
+ if (!remaining) {
+ throw new address_error_1.AddressError('Error parsing groups');
+ }
+ this.elidedGroups = remaining;
+ this.elisionBegin = first.length;
+ this.elisionEnd = first.length + this.elidedGroups;
+ groups = groups.concat(first);
+ for (let i = 0; i < remaining; i++) {
+ groups.push('0');
+ }
+ groups = groups.concat(last);
+ }
+ else if (halves.length === 1) {
+ groups = address.split(':');
+ this.elidedGroups = 0;
+ }
+ else {
+ throw new address_error_1.AddressError('Too many :: groups found');
+ }
+ groups = groups.map((group) => parseInt(group, 16).toString(16));
+ if (groups.length !== this.groups) {
+ throw new address_error_1.AddressError('Incorrect number of groups found');
+ }
+ return groups;
+ }
+ /**
+ * Return the canonical form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ canonicalForm() {
+ return this.parsedAddress.map(paddedHex).join(':');
+ }
+ /**
+ * Return the decimal form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ decimal() {
+ return this.parsedAddress.map((n) => parseInt(n, 16).toString(10).padStart(5, '0')).join(':');
+ }
+ /**
+ * Return the address as a BigInt
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ bigInt() {
+ return BigInt(`0x${this.parsedAddress.map(paddedHex).join('')}`);
+ }
+ /**
+ * Return the last two groups of this address as an IPv4 address string
+ * @memberof Address6
+ * @instance
+ * @returns {Address4}
+ * @example
+ * var address = new Address6('2001:4860:4001::1825:bf11');
+ * address.to4().correctForm(); // '24.37.191.17'
+ */
+ to4() {
+ const binary = this.binaryZeroPad().split('');
+ return ipv4_1.Address4.fromHex(BigInt(`0b${binary.slice(96, 128).join('')}`).toString(16));
+ }
+ /**
+ * Return the v4-in-v6 form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ to4in6() {
+ const address4 = this.to4();
+ const address6 = new Address6(this.parsedAddress.slice(0, 6).join(':'), 6);
+ const correct = address6.correctForm();
+ let infix = '';
+ if (!/:$/.test(correct)) {
+ infix = ':';
+ }
+ return correct + infix + address4.address;
+ }
+ /**
+ * Return an object containing the Teredo properties of the address
+ * @memberof Address6
+ * @instance
+ * @returns {Object}
+ */
+ inspectTeredo() {
+ /*
+ - Bits 0 to 31 are set to the Teredo prefix (normally 2001:0000::/32).
+ - Bits 32 to 63 embed the primary IPv4 address of the Teredo server that
+ is used.
+ - Bits 64 to 79 can be used to define some flags. Currently only the
+ higher order bit is used; it is set to 1 if the Teredo client is
+ located behind a cone NAT, 0 otherwise. For Microsoft's Windows Vista
+ and Windows Server 2008 implementations, more bits are used. In those
+ implementations, the format for these 16 bits is "CRAAAAUG AAAAAAAA",
+ where "C" remains the "Cone" flag. The "R" bit is reserved for future
+ use. The "U" bit is for the Universal/Local flag (set to 0). The "G" bit
+ is Individual/Group flag (set to 0). The A bits are set to a 12-bit
+ randomly generated number chosen by the Teredo client to introduce
+ additional protection for the Teredo node against IPv6-based scanning
+ attacks.
+ - Bits 80 to 95 contains the obfuscated UDP port number. This is the
+ port number that is mapped by the NAT to the Teredo client with all
+ bits inverted.
+ - Bits 96 to 127 contains the obfuscated IPv4 address. This is the
+ public IPv4 address of the NAT with all bits inverted.
+ */
+ const prefix = this.getBitsBase16(0, 32);
+ const bitsForUdpPort = this.getBits(80, 96);
+ // eslint-disable-next-line no-bitwise
+ const udpPort = (bitsForUdpPort ^ BigInt('0xffff')).toString();
+ const server4 = ipv4_1.Address4.fromHex(this.getBitsBase16(32, 64));
+ const bitsForClient4 = this.getBits(96, 128);
+ // eslint-disable-next-line no-bitwise
+ const client4 = ipv4_1.Address4.fromHex((bitsForClient4 ^ BigInt('0xffffffff')).toString(16));
+ const flagsBase2 = this.getBitsBase2(64, 80);
+ const coneNat = (0, common_1.testBit)(flagsBase2, 15);
+ const reserved = (0, common_1.testBit)(flagsBase2, 14);
+ const groupIndividual = (0, common_1.testBit)(flagsBase2, 8);
+ const universalLocal = (0, common_1.testBit)(flagsBase2, 9);
+ const nonce = BigInt(`0b${flagsBase2.slice(2, 6) + flagsBase2.slice(8, 16)}`).toString(10);
+ return {
+ prefix: `${prefix.slice(0, 4)}:${prefix.slice(4, 8)}`,
+ server4: server4.address,
+ client4: client4.address,
+ flags: flagsBase2,
+ coneNat,
+ microsoft: {
+ reserved,
+ universalLocal,
+ groupIndividual,
+ nonce,
+ },
+ udpPort,
+ };
+ }
+ /**
+ * Return an object containing the 6to4 properties of the address
+ * @memberof Address6
+ * @instance
+ * @returns {Object}
+ */
+ inspect6to4() {
+ /*
+ - Bits 0 to 15 are set to the 6to4 prefix (2002::/16).
+ - Bits 16 to 48 embed the IPv4 address of the 6to4 gateway that is used.
+ */
+ const prefix = this.getBitsBase16(0, 16);
+ const gateway = ipv4_1.Address4.fromHex(this.getBitsBase16(16, 48));
+ return {
+ prefix: prefix.slice(0, 4),
+ gateway: gateway.address,
+ };
+ }
+ /**
+ * Return a v6 6to4 address from a v6 v4inv6 address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ to6to4() {
+ if (!this.is4()) {
+ return null;
+ }
+ const addr6to4 = [
+ '2002',
+ this.getBitsBase16(96, 112),
+ this.getBitsBase16(112, 128),
+ '',
+ '/16',
+ ].join(':');
+ return new Address6(addr6to4);
+ }
+ /**
+ * Return a byte array
+ * @memberof Address6
+ * @instance
+ * @returns {Array}
+ */
+ toByteArray() {
+ const valueWithoutPadding = this.bigInt().toString(16);
+ const leadingPad = '0'.repeat(valueWithoutPadding.length % 2);
+ const value = `${leadingPad}${valueWithoutPadding}`;
+ const bytes = [];
+ for (let i = 0, length = value.length; i < length; i += 2) {
+ bytes.push(parseInt(value.substring(i, i + 2), 16));
+ }
+ return bytes;
+ }
+ /**
+ * Return an unsigned byte array
+ * @memberof Address6
+ * @instance
+ * @returns {Array}
+ */
+ toUnsignedByteArray() {
+ return this.toByteArray().map(unsignByte);
+ }
+ /**
+ * Convert a byte array to an Address6 object
+ * @memberof Address6
+ * @static
+ * @returns {Address6}
+ */
+ static fromByteArray(bytes) {
+ return this.fromUnsignedByteArray(bytes.map(unsignByte));
+ }
+ /**
+ * Convert an unsigned byte array to an Address6 object
+ * @memberof Address6
+ * @static
+ * @returns {Address6}
+ */
+ static fromUnsignedByteArray(bytes) {
+ const BYTE_MAX = BigInt('256');
+ let result = BigInt('0');
+ let multiplier = BigInt('1');
+ for (let i = bytes.length - 1; i >= 0; i--) {
+ result += multiplier * BigInt(bytes[i].toString(10));
+ multiplier *= BYTE_MAX;
+ }
+ return Address6.fromBigInt(result);
+ }
+ /**
+ * Returns true if the address is in the canonical form, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isCanonical() {
+ return this.addressMinusSuffix === this.canonicalForm();
+ }
+ /**
+ * Returns true if the address is a link local address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isLinkLocal() {
+ // Zeroes are required, i.e. we can't check isInSubnet with 'fe80::/10'
+ if (this.getBitsBase2(0, 64) ===
+ '1111111010000000000000000000000000000000000000000000000000000000') {
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Returns true if the address is a multicast address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isMulticast() {
+ return this.getType() === 'Multicast';
+ }
+ /**
+ * Returns true if the address is a v4-in-v6 address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ is4() {
+ return this.v4;
+ }
+ /**
+ * Returns true if the address is a Teredo address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isTeredo() {
+ return this.isInSubnet(new Address6('2001::/32'));
+ }
+ /**
+ * Returns true if the address is a 6to4 address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ is6to4() {
+ return this.isInSubnet(new Address6('2002::/16'));
+ }
+ /**
+ * Returns true if the address is a loopback address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isLoopback() {
+ return this.getType() === 'Loopback';
+ }
+ // #endregion
+ // #region HTML
+ /**
+ * @returns {String} the address in link form with a default port of 80
+ */
+ href(optionalPort) {
+ if (optionalPort === undefined) {
+ optionalPort = '';
+ }
+ else {
+ optionalPort = `:${optionalPort}`;
+ }
+ return `http://[${this.correctForm()}]${optionalPort}/`;
+ }
+ /**
+ * @returns {String} a link suitable for conveying the address via a URL hash
+ */
+ link(options) {
+ if (!options) {
+ options = {};
+ }
+ if (options.className === undefined) {
+ options.className = '';
+ }
+ if (options.prefix === undefined) {
+ options.prefix = '/#address=';
+ }
+ if (options.v4 === undefined) {
+ options.v4 = false;
+ }
+ let formFunction = this.correctForm;
+ if (options.v4) {
+ formFunction = this.to4in6;
+ }
+ const form = formFunction.call(this);
+ if (options.className) {
+ return `${form} `;
+ }
+ return `${form} `;
+ }
+ /**
+ * Groups an address
+ * @returns {String}
+ */
+ group() {
+ if (this.elidedGroups === 0) {
+ // The simple case
+ return helpers.simpleGroup(this.address).join(':');
+ }
+ assert(typeof this.elidedGroups === 'number');
+ assert(typeof this.elisionBegin === 'number');
+ // The elided case
+ const output = [];
+ const [left, right] = this.address.split('::');
+ if (left.length) {
+ output.push(...helpers.simpleGroup(left));
+ }
+ else {
+ output.push('');
+ }
+ const classes = ['hover-group'];
+ for (let i = this.elisionBegin; i < this.elisionBegin + this.elidedGroups; i++) {
+ classes.push(`group-${i}`);
+ }
+ output.push(` `);
+ if (right.length) {
+ output.push(...helpers.simpleGroup(right, this.elisionEnd));
+ }
+ else {
+ output.push('');
+ }
+ if (this.is4()) {
+ assert(this.address4 instanceof ipv4_1.Address4);
+ output.pop();
+ output.push(this.address4.groupForV6());
+ }
+ return output.join(':');
+ }
+ // #endregion
+ // #region Regular expressions
+ /**
+ * Generate a regular expression string that can be used to find or validate
+ * all variations of this address
+ * @memberof Address6
+ * @instance
+ * @param {boolean} substringSearch
+ * @returns {string}
+ */
+ regularExpressionString(substringSearch = false) {
+ let output = [];
+ // TODO: revisit why this is necessary
+ const address6 = new Address6(this.correctForm());
+ if (address6.elidedGroups === 0) {
+ // The simple case
+ output.push((0, regular_expressions_1.simpleRegularExpression)(address6.parsedAddress));
+ }
+ else if (address6.elidedGroups === constants6.GROUPS) {
+ // A completely elided address
+ output.push((0, regular_expressions_1.possibleElisions)(constants6.GROUPS));
+ }
+ else {
+ // A partially elided address
+ const halves = address6.address.split('::');
+ if (halves[0].length) {
+ output.push((0, regular_expressions_1.simpleRegularExpression)(halves[0].split(':')));
+ }
+ assert(typeof address6.elidedGroups === 'number');
+ output.push((0, regular_expressions_1.possibleElisions)(address6.elidedGroups, halves[0].length !== 0, halves[1].length !== 0));
+ if (halves[1].length) {
+ output.push((0, regular_expressions_1.simpleRegularExpression)(halves[1].split(':')));
+ }
+ output = [output.join(':')];
+ }
+ if (!substringSearch) {
+ output = [
+ '(?=^|',
+ regular_expressions_1.ADDRESS_BOUNDARY,
+ '|[^\\w\\:])(',
+ ...output,
+ ')(?=[^\\w\\:]|',
+ regular_expressions_1.ADDRESS_BOUNDARY,
+ '|$)',
+ ];
+ }
+ return output.join('');
+ }
+ /**
+ * Generate a regular expression that can be used to find or validate all
+ * variations of this address.
+ * @memberof Address6
+ * @instance
+ * @param {boolean} substringSearch
+ * @returns {RegExp}
+ */
+ regularExpression(substringSearch = false) {
+ return new RegExp(this.regularExpressionString(substringSearch), 'i');
+ }
+}
+exports.Address6 = Address6;
+//# sourceMappingURL=ipv6.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.js.map
new file mode 100644
index 000000000..05d59a086
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/ipv6.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"ipv6.js","sourceRoot":"","sources":["../src/ipv6.ts"],"names":[],"mappings":";AAAA,yCAAyC;AACzC,sCAAsC;;;;;;;;;;;;;;;;;;;;;;;;;;AAEtC,iDAAmC;AACnC,2DAA6C;AAC7C,2DAA6C;AAC7C,sDAAwC;AACxC,iCAAkC;AAClC,kEAIkC;AAClC,mDAA+C;AAC/C,qCAAmC;AAEnC,SAAS,MAAM,CAAC,SAAc;IAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,MAAc;IAC/B,MAAM,CAAC,GAAG,cAAc,CAAC;IAEzB,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAS;IACnC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,uCAAuC,CAAC,CAAC;IAC5E,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,uCAAuC,CAAC,CAAC;IAEvE,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,OAAiB,EAAE,KAAe;IACjD,MAAM,EAAE,GAAG,EAAE,CAAC;IACd,MAAM,EAAE,GAAG,EAAE,CAAC;IACd,IAAI,CAAC,CAAC;IAEN,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACjB,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;aAAM,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,sCAAsC;IACtC,OAAO,CAAC,GAAG,IAAI,CAAC;AAClB,CAAC;AAsBD;;;;;;;GAOG;AACH,MAAa,QAAQ;IAgBnB,YAAY,OAAe,EAAE,cAAuB;QAbpD,uBAAkB,GAAW,EAAE,CAAC;QAOhC,iBAAY,GAAW,EAAE,CAAC;QAC1B,WAAM,GAAW,MAAM,CAAC;QACxB,eAAU,GAAW,GAAG,CAAC;QACzB,OAAE,GAAY,KAAK,CAAC;QACpB,SAAI,GAAW,EAAE,CAAC;QAu0BlB,qBAAqB;QACrB;;;;;WAKG;QACH,eAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAE/B;;;;;WAKG;QACH,cAAS,GAAG,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAn1B5C,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC;QAC/B,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAEvB,MAAM,MAAM,GAAG,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEzD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC/C,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAEpC,IACE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;gBAC7B,IAAI,CAAC,UAAU,GAAG,CAAC;gBACnB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,IAAI,EACjC,CAAC;gBACD,MAAM,IAAI,4BAAY,CAAC,sBAAsB,CAAC,CAAC;YACjD,CAAC;YAED,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,4BAAY,CAAC,sBAAsB,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,IAAI,GAAG,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAErD,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAEpB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;QAElC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAAe;QAC5B,IAAI,CAAC;YACH,kCAAkC;YAClC,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;YAEtB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,UAAU,CAAC,MAAc;QAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,CAAC;QAEN,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,CAAC,GAAW;QACxB,IAAI,IAAY,CAAC;QACjB,IAAI,IAAI,GAA2B,IAAI,CAAC;QACxC,IAAI,MAAuB,CAAC;QAE5B,iDAAiD;QACjD,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACxD,MAAM,GAAG,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE/C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO;oBACL,KAAK,EAAE,mCAAmC;oBAC1C,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,IAAI;iBACX,CAAC;YACJ,CAAC;YAED,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACjB,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACjB,uCAAuC;QACzC,CAAC;aAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACnC,6BAA6B;YAC7B,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;YAEzC,oBAAoB;YACpB,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAErC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO;oBACL,KAAK,EAAE,kCAAkC;oBACzC,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,IAAI;iBACX,CAAC;YACJ,CAAC;YAED,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACjB,yEAAyE;QAC3E,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,GAAG,CAAC;QACb,CAAC;QAED,6CAA6C;QAC7C,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAE1B,6BAA6B;YAC7B,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;gBAC7B,IAAI,GAAG,IAAI,CAAC;YACd,CAAC;QACH,CAAC;aAAM,CAAC;YACN,oCAAoC;YACpC,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC;YAC3B,IAAI;SACL,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,YAAY,CAAC,OAAe;QACjC,MAAM,QAAQ,GAAG,IAAI,eAAQ,CAAC,OAAO,CAAC,CAAC;QAEvC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAExE,OAAO,IAAI,QAAQ,CAAC,UAAU,QAAQ,CAAC,WAAW,EAAE,IAAI,KAAK,EAAE,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,eAAuB;QACrC,yCAAyC;QACzC,IAAI,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;QAC/D,MAAM,eAAe,GAAG,CAAC,CAAC;QAE1B,kEAAkE;QAClE,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;YAC1B,MAAM,IAAI,4BAAY,CAAC,0BAA0B,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QAE3C,KAAK,IAAI,CAAC,GAAG,eAAe,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1B,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEzB,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,sBAAsB;QACpB,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,mBAAmB,CAAC;IACrE,CAAC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,OAAe,IAAI,CAAC,UAAU;QACjC,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;OAMG;IACH,8DAA8D;IAC9D,eAAe,CAAC,aAAqB,GAAG;QACtC,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QACxD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,YAAY,GAAG,aAAa,GAAG,UAAU,CAAC;QAEhD,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,GAAG,CAAC;QACb,CAAC;QAED,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACH,aAAa;QACX,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC;IAED;;;;;;OAMG;IACH,YAAY;QACV,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;OAMG;IACH,qBAAqB;QACnB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,MAAM,CAAC,CAAC;IAC5D,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC;IAED;;;;;;OAMG;IACH,UAAU;QACR,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;OAMG;IACH,mBAAmB;QACjB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,CAAC;IAC1D,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACN,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAE/E,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,gBAAgB,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;YAClE,KAAK,GAAG,QAAQ,CAAC;QACnB,CAAC;QAED,OAAO,KAAK,IAAI,SAAS,CAAC;IAC5B,CAAC;IAED;;;;;OAKG;IACH,OAAO;QACL,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBAC1C,OAAO,UAAU,CAAC,KAAK,CAAC,MAAM,CAAW,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,KAAa,EAAE,GAAW;QAChC,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,KAAa,EAAE,GAAW;QACrC,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChD,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,KAAa,EAAE,GAAW;QACtC,MAAM,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC;QAE3B,IAAI,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aAC5B,QAAQ,CAAC,EAAE,CAAC;aACZ,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,iBAAiB;QACf,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;;;OAOG;IACH,WAAW,CAAC,OAAmC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,EAAE,CAAC;QACf,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QAEnD,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE;aAClC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;aACjB,KAAK,CAAC,EAAE,CAAC;aACT,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;aACpB,OAAO,EAAE;aACT,IAAI,CAAC,GAAG,CAAC,CAAC;QAEb,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACvB,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,OAAO,GAAG,QAAQ,YAAY,CAAC;QACjC,CAAC;QAED,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,IAAI,CAAC,CAAC;QACN,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,MAAM,MAAM,GAAG,EAAE,CAAC;QAElB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAElD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,WAAW,EAAE,CAAC;YAChB,CAAC;YAED,IAAI,KAAK,KAAK,CAAC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;gBACnC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;oBACpB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACxC,CAAC;gBAED,WAAW,GAAG,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAED,qCAAqC;QACrC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEvD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAW,CAAC,CAAC;YAEtE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtD,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;QAC9B,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC5B,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAED,IAAI,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE/B,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC7C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;QACxD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEzC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;;;OAUG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAClE,CAAC;IAED,sDAAsD;IACtD,SAAS,CAAC,OAAe;QACvB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEtC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAExD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAElD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACpD,MAAM,IAAI,4BAAY,CACpB,2CAA2C,EAC3C,OAAO,CAAC,OAAO,CACb,UAAU,CAAC,UAAU,EACrB,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAC9D,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;YAEf,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAErD,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,sBAAsB;IACtB,KAAK,CAAC,OAAe;QACnB,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAElC,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QAElE,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,IAAI,4BAAY,CACpB,gBACE,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EACnC,yBAAyB,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EACjD,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,qCAAqC,CAAC,CACrF,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAE5D,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,4BAAY,CACpB,yBAAyB,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAC9C,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,qCAAqC,CAAC,CAClF,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,GAAa,EAAE,CAAC;QAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAEhC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC1C,KAAK,GAAG,EAAE,CAAC;YACb,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBACxC,IAAI,GAAG,EAAE,CAAC;YACZ,CAAC;YAED,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YAE7D,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,IAAI,4BAAY,CAAC,sBAAsB,CAAC,CAAC;YACjD,CAAC;YAED,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAE9B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC;YACjC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC;YAEnD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YAED,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAE5B,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,4BAAY,CAAC,0BAA0B,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAEzE,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,IAAI,4BAAY,CAAC,kCAAkC,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACH,OAAO;QACL,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChG,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;;;OAQG;IACH,GAAG;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAE9C,OAAO,eAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACtF,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAE3E,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QAEvC,IAAI,KAAK,GAAG,EAAE,CAAC;QAEf,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,KAAK,GAAG,GAAG,CAAC;QACd,CAAC;QAED,OAAO,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,aAAa;QACX;;;;;;;;;;;;;;;;;;;;UAoBE;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEzC,MAAM,cAAc,GAAW,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACpD,sCAAsC;QACtC,MAAM,OAAO,GAAG,CAAC,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAE/D,MAAM,OAAO,GAAG,eAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAE7D,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC7C,sCAAsC;QACtC,MAAM,OAAO,GAAG,eAAQ,CAAC,OAAO,CAAC,CAAC,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAEvF,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAE7C,MAAM,OAAO,GAAG,IAAA,gBAAO,EAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAA,gBAAO,EAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,eAAe,GAAG,IAAA,gBAAO,EAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,cAAc,GAAG,IAAA,gBAAO,EAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE3F,OAAO;YACL,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;YACrD,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,UAAU;YACjB,OAAO;YACP,SAAS,EAAE;gBACT,QAAQ;gBACR,cAAc;gBACd,eAAe;gBACf,KAAK;aACN;YACD,OAAO;SACR,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT;;;UAGE;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEzC,MAAM,OAAO,GAAG,eAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAE7D,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,QAAQ,GAAG;YACf,MAAM;YACN,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC;YAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;YAC5B,EAAE;YACF,KAAK;SACN,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEZ,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE9D,MAAM,KAAK,GAAG,GAAG,UAAU,GAAG,mBAAmB,EAAE,CAAC;QAEpD,MAAM,KAAK,GAAG,EAAE,CAAC;QACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1D,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACtD,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACH,mBAAmB;QACjB,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,aAAa,CAAC,KAAiB;QACpC,OAAO,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAiB;QAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAE7B,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,IAAI,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YAErD,UAAU,IAAI,QAAQ,CAAC;QACzB,CAAC;QAED,OAAO,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAmBD;;;;;OAKG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,kBAAkB,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;IAC1D,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,uEAAuE;QACvE,IACE,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC;YACxB,kEAAkE,EAClE,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,WAAW,CAAC;IACxC,CAAC;IAED;;;;;OAKG;IACH,GAAG;QACD,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;IACpD,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;IACpD,CAAC;IAED;;;;;OAKG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC;IACvC,CAAC;IACD,aAAa;IAEb,eAAe;IACf;;OAEG;IACH,IAAI,CAAC,YAA8B;QACjC,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,YAAY,GAAG,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;QACpC,CAAC;QAED,OAAO,WAAW,IAAI,CAAC,WAAW,EAAE,IAAI,YAAY,GAAG,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAA+D;QAClE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,EAAE,CAAC;QACf,CAAC;QAED,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,CAAC,SAAS,GAAG,EAAE,CAAC;QACzB,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,GAAG,YAAY,CAAC;QAChC,CAAC;QAED,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,CAAC,EAAE,GAAG,KAAK,CAAC;QACrB,CAAC;QAED,IAAI,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC;QAEpC,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;YACf,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7B,CAAC;QAED,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAErC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO,YAAY,OAAO,CAAC,MAAM,GAAG,IAAI,YAAY,OAAO,CAAC,SAAS,KAAK,IAAI,MAAM,CAAC;QACvF,CAAC;QAED,OAAO,YAAY,OAAO,CAAC,MAAM,GAAG,IAAI,KAAK,IAAI,MAAM,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC,EAAE,CAAC;YAC5B,kBAAkB;YAClB,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,CAAC,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC;QAE9C,kBAAkB;QAClB,MAAM,MAAM,GAAG,EAAE,CAAC;QAElB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,CAAC,aAAa,CAAC,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/E,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,gBAAgB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAE1D,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,QAAQ,YAAY,eAAQ,CAAC,CAAC;YAE1C,MAAM,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QAC1C,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IACD,aAAa;IAEb,8BAA8B;IAC9B;;;;;;;OAOG;IACH,uBAAuB,CAAiB,kBAA2B,KAAK;QACtE,IAAI,MAAM,GAAa,EAAE,CAAC;QAE1B,sCAAsC;QACtC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAElD,IAAI,QAAQ,CAAC,YAAY,KAAK,CAAC,EAAE,CAAC;YAChC,kBAAkB;YAClB,MAAM,CAAC,IAAI,CAAC,IAAA,6CAAuB,EAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;QAC/D,CAAC;aAAM,IAAI,QAAQ,CAAC,YAAY,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;YACvD,8BAA8B;YAC9B,MAAM,CAAC,IAAI,CAAC,IAAA,sCAAgB,EAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,6BAA6B;YAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAE5C,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;gBACrB,MAAM,CAAC,IAAI,CAAC,IAAA,6CAAuB,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,CAAC,OAAO,QAAQ,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC;YAElD,MAAM,CAAC,IAAI,CACT,IAAA,sCAAgB,EAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CACxF,CAAC;YAEF,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;gBACrB,MAAM,CAAC,IAAI,CAAC,IAAA,6CAAuB,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,GAAG;gBACP,OAAO;gBACP,sCAAgB;gBAChB,cAAc;gBACd,GAAG,MAAM;gBACT,gBAAgB;gBAChB,sCAAgB;gBAChB,KAAK;aACN,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED;;;;;;;OAOG;IACH,iBAAiB,CAAiB,kBAA2B,KAAK;QAChE,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,eAAe,CAAC,EAAE,GAAG,CAAC,CAAC;IACxE,CAAC;CAEF;AA5lCD,4BA4lCC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.d.ts
new file mode 100644
index 000000000..4e85d4a4b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.d.ts
@@ -0,0 +1,5 @@
+export declare const BITS = 32;
+export declare const GROUPS = 4;
+export declare const RE_ADDRESS: RegExp;
+export declare const RE_SUBNET_STRING: RegExp;
+//# sourceMappingURL=constants.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.d.ts.map
new file mode 100644
index 000000000..ea60cedb7
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/v4/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,MAAM,IAAI,CAAC;AAExB,eAAO,MAAM,UAAU,QAC8I,CAAC;AAEtK,eAAO,MAAM,gBAAgB,QAAe,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.js
new file mode 100644
index 000000000..6fa2518f9
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.js
@@ -0,0 +1,8 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.RE_SUBNET_STRING = exports.RE_ADDRESS = exports.GROUPS = exports.BITS = void 0;
+exports.BITS = 32;
+exports.GROUPS = 4;
+exports.RE_ADDRESS = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;
+exports.RE_SUBNET_STRING = /\/\d{1,2}$/;
+//# sourceMappingURL=constants.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.js.map
new file mode 100644
index 000000000..0a96f8697
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v4/constants.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/v4/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,IAAI,GAAG,EAAE,CAAC;AACV,QAAA,MAAM,GAAG,CAAC,CAAC;AAEX,QAAA,UAAU,GACrB,mKAAmK,CAAC;AAEzJ,QAAA,gBAAgB,GAAG,YAAY,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.d.ts
new file mode 100644
index 000000000..130237531
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.d.ts
@@ -0,0 +1,45 @@
+export declare const BITS = 128;
+export declare const GROUPS = 8;
+/**
+ * Represents IPv6 address scopes
+ * @memberof Address6
+ * @static
+ */
+export declare const SCOPES: {
+ [key: number]: string | undefined;
+};
+/**
+ * Represents IPv6 address types
+ * @memberof Address6
+ * @static
+ */
+export declare const TYPES: {
+ [key: string]: string | undefined;
+};
+/**
+ * A regular expression that matches bad characters in an IPv6 address
+ * @memberof Address6
+ * @static
+ */
+export declare const RE_BAD_CHARACTERS: RegExp;
+/**
+ * A regular expression that matches an incorrect IPv6 address
+ * @memberof Address6
+ * @static
+ */
+export declare const RE_BAD_ADDRESS: RegExp;
+/**
+ * A regular expression that matches an IPv6 subnet
+ * @memberof Address6
+ * @static
+ */
+export declare const RE_SUBNET_STRING: RegExp;
+/**
+ * A regular expression that matches an IPv6 zone
+ * @memberof Address6
+ * @static
+ */
+export declare const RE_ZONE_STRING: RegExp;
+export declare const RE_URL: RegExp;
+export declare const RE_URL_WITH_PORT: RegExp;
+//# sourceMappingURL=constants.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.d.ts.map
new file mode 100644
index 000000000..0f4f412f9
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/v6/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,MAAM,CAAC;AACxB,eAAO,MAAM,MAAM,IAAI,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAS9C,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,KAAK,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAuB7C,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAAqB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAA6C,CAAC;AAEzE;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAqB,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAS,CAAC;AAErC,eAAO,MAAM,MAAM,QAAgC,CAAC;AACpD,eAAO,MAAM,gBAAgB,QAAkC,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.js
new file mode 100644
index 000000000..0abc423e0
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.js
@@ -0,0 +1,76 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.RE_URL_WITH_PORT = exports.RE_URL = exports.RE_ZONE_STRING = exports.RE_SUBNET_STRING = exports.RE_BAD_ADDRESS = exports.RE_BAD_CHARACTERS = exports.TYPES = exports.SCOPES = exports.GROUPS = exports.BITS = void 0;
+exports.BITS = 128;
+exports.GROUPS = 8;
+/**
+ * Represents IPv6 address scopes
+ * @memberof Address6
+ * @static
+ */
+exports.SCOPES = {
+ 0: 'Reserved',
+ 1: 'Interface local',
+ 2: 'Link local',
+ 4: 'Admin local',
+ 5: 'Site local',
+ 8: 'Organization local',
+ 14: 'Global',
+ 15: 'Reserved',
+};
+/**
+ * Represents IPv6 address types
+ * @memberof Address6
+ * @static
+ */
+exports.TYPES = {
+ 'ff01::1/128': 'Multicast (All nodes on this interface)',
+ 'ff01::2/128': 'Multicast (All routers on this interface)',
+ 'ff02::1/128': 'Multicast (All nodes on this link)',
+ 'ff02::2/128': 'Multicast (All routers on this link)',
+ 'ff05::2/128': 'Multicast (All routers in this site)',
+ 'ff02::5/128': 'Multicast (OSPFv3 AllSPF routers)',
+ 'ff02::6/128': 'Multicast (OSPFv3 AllDR routers)',
+ 'ff02::9/128': 'Multicast (RIP routers)',
+ 'ff02::a/128': 'Multicast (EIGRP routers)',
+ 'ff02::d/128': 'Multicast (PIM routers)',
+ 'ff02::16/128': 'Multicast (MLDv2 reports)',
+ 'ff01::fb/128': 'Multicast (mDNSv6)',
+ 'ff02::fb/128': 'Multicast (mDNSv6)',
+ 'ff05::fb/128': 'Multicast (mDNSv6)',
+ 'ff02::1:2/128': 'Multicast (All DHCP servers and relay agents on this link)',
+ 'ff05::1:2/128': 'Multicast (All DHCP servers and relay agents in this site)',
+ 'ff02::1:3/128': 'Multicast (All DHCP servers on this link)',
+ 'ff05::1:3/128': 'Multicast (All DHCP servers in this site)',
+ '::/128': 'Unspecified',
+ '::1/128': 'Loopback',
+ 'ff00::/8': 'Multicast',
+ 'fe80::/10': 'Link-local unicast',
+};
+/**
+ * A regular expression that matches bad characters in an IPv6 address
+ * @memberof Address6
+ * @static
+ */
+exports.RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi;
+/**
+ * A regular expression that matches an incorrect IPv6 address
+ * @memberof Address6
+ * @static
+ */
+exports.RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\/$)/gi;
+/**
+ * A regular expression that matches an IPv6 subnet
+ * @memberof Address6
+ * @static
+ */
+exports.RE_SUBNET_STRING = /\/\d{1,3}(?=%|$)/;
+/**
+ * A regular expression that matches an IPv6 zone
+ * @memberof Address6
+ * @static
+ */
+exports.RE_ZONE_STRING = /%.*$/;
+exports.RE_URL = /^\[{0,1}([0-9a-f:]+)\]{0,1}/;
+exports.RE_URL_WITH_PORT = /\[([0-9a-f:]+)\]:([0-9]{1,5})/;
+//# sourceMappingURL=constants.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.js.map
new file mode 100644
index 000000000..1d313e0c5
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/constants.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/v6/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,IAAI,GAAG,GAAG,CAAC;AACX,QAAA,MAAM,GAAG,CAAC,CAAC;AAExB;;;;GAIG;AACU,QAAA,MAAM,GAA0C;IAC3D,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,iBAAiB;IACpB,CAAC,EAAE,YAAY;IACf,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,YAAY;IACf,CAAC,EAAE,oBAAoB;IACvB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,UAAU;CACN,CAAC;AAEX;;;;GAIG;AACU,QAAA,KAAK,GAA0C;IAC1D,aAAa,EAAE,yCAAyC;IACxD,aAAa,EAAE,2CAA2C;IAC1D,aAAa,EAAE,oCAAoC;IACnD,aAAa,EAAE,sCAAsC;IACrD,aAAa,EAAE,sCAAsC;IACrD,aAAa,EAAE,mCAAmC;IAClD,aAAa,EAAE,kCAAkC;IACjD,aAAa,EAAE,yBAAyB;IACxC,aAAa,EAAE,2BAA2B;IAC1C,aAAa,EAAE,yBAAyB;IACxC,cAAc,EAAE,2BAA2B;IAC3C,cAAc,EAAE,oBAAoB;IACpC,cAAc,EAAE,oBAAoB;IACpC,cAAc,EAAE,oBAAoB;IACpC,eAAe,EAAE,4DAA4D;IAC7E,eAAe,EAAE,4DAA4D;IAC7E,eAAe,EAAE,2CAA2C;IAC5D,eAAe,EAAE,2CAA2C;IAC5D,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,UAAU;IACrB,UAAU,EAAE,WAAW;IACvB,WAAW,EAAE,oBAAoB;CACzB,CAAC;AAEX;;;;GAIG;AACU,QAAA,iBAAiB,GAAG,kBAAkB,CAAC;AAEpD;;;;GAIG;AACU,QAAA,cAAc,GAAG,0CAA0C,CAAC;AAEzE;;;;GAIG;AACU,QAAA,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD;;;;GAIG;AACU,QAAA,cAAc,GAAG,MAAM,CAAC;AAExB,QAAA,MAAM,GAAG,6BAA6B,CAAC;AACvC,QAAA,gBAAgB,GAAG,+BAA+B,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.d.ts
new file mode 100644
index 000000000..e72f0311d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.d.ts
@@ -0,0 +1,18 @@
+/**
+ * @returns {String} the string with all zeroes contained in a
+ */
+export declare function spanAllZeroes(s: string): string;
+/**
+ * @returns {String} the string with each character contained in a
+ */
+export declare function spanAll(s: string, offset?: number): string;
+/**
+ * @returns {String} the string with leading zeroes contained in a
+ */
+export declare function spanLeadingZeroes(address: string): string;
+/**
+ * Groups an address
+ * @returns {String} a grouped address
+ */
+export declare function simpleGroup(addressString: string, offset?: number): string[];
+//# sourceMappingURL=helpers.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.d.ts.map
new file mode 100644
index 000000000..823f26e21
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/v6/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,GAAG,MAAM,CAQ7D;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,GAAG,MAAM,EAAE,CAU/E"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.js
new file mode 100644
index 000000000..fafca0c27
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.js
@@ -0,0 +1,45 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.spanAllZeroes = spanAllZeroes;
+exports.spanAll = spanAll;
+exports.spanLeadingZeroes = spanLeadingZeroes;
+exports.simpleGroup = simpleGroup;
+/**
+ * @returns {String} the string with all zeroes contained in a
+ */
+function spanAllZeroes(s) {
+ return s.replace(/(0+)/g, '$1 ');
+}
+/**
+ * @returns {String} the string with each character contained in a
+ */
+function spanAll(s, offset = 0) {
+ const letters = s.split('');
+ return letters
+ .map((n, i) => `${spanAllZeroes(n)} `)
+ .join('');
+}
+function spanLeadingZeroesSimple(group) {
+ return group.replace(/^(0+)/, '$1 ');
+}
+/**
+ * @returns {String} the string with leading zeroes contained in a
+ */
+function spanLeadingZeroes(address) {
+ const groups = address.split(':');
+ return groups.map((g) => spanLeadingZeroesSimple(g)).join(':');
+}
+/**
+ * Groups an address
+ * @returns {String} a grouped address
+ */
+function simpleGroup(addressString, offset = 0) {
+ const groups = addressString.split(':');
+ return groups.map((g, i) => {
+ if (/group-v4/.test(g)) {
+ return g;
+ }
+ return `${spanLeadingZeroesSimple(g)} `;
+ });
+}
+//# sourceMappingURL=helpers.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.js.map
new file mode 100644
index 000000000..c89545538
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/helpers.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/v6/helpers.ts"],"names":[],"mappings":";;AAGA,sCAEC;AAKD,0BAQC;AASD,8CAIC;AAMD,kCAUC;AA/CD;;GAEG;AACH,SAAgB,aAAa,CAAC,CAAS;IACrC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAgB,OAAO,CAAC,CAAS,EAAE,SAAiB,CAAC;IACnD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAE5B,OAAO,OAAO;SACX,GAAG,CACF,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,4BAA4B,CAAC,aAAa,CAAC,GAAG,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAC7F;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa;IAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,OAAe;IAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAElC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,aAAqB,EAAE,SAAiB,CAAC;IACnE,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAExC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACzB,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,CAAC;QACX,CAAC;QAED,OAAO,kCAAkC,CAAC,GAAG,MAAM,KAAK,uBAAuB,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,CAAC,CAAC,CAAC;AACL,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.d.ts
new file mode 100644
index 000000000..181b91e81
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.d.ts
@@ -0,0 +1,6 @@
+export declare function groupPossibilities(possibilities: string[]): string;
+export declare function padGroup(group: string): string;
+export declare const ADDRESS_BOUNDARY = "[^A-Fa-f0-9:]";
+export declare function simpleRegularExpression(groups: string[]): string;
+export declare function possibleElisions(elidedGroups: number, moreLeft?: boolean, moreRight?: boolean): string;
+//# sourceMappingURL=regular-expressions.d.ts.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.d.ts.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.d.ts.map
new file mode 100644
index 000000000..20a93f7d9
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"regular-expressions.d.ts","sourceRoot":"","sources":["../../src/v6/regular-expressions.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAElE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,UA+BvD;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,OAAO,EAClB,SAAS,CAAC,EAAE,OAAO,GAClB,MAAM,CAwCR"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.js
new file mode 100644
index 000000000..a2c514593
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.js
@@ -0,0 +1,95 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.ADDRESS_BOUNDARY = void 0;
+exports.groupPossibilities = groupPossibilities;
+exports.padGroup = padGroup;
+exports.simpleRegularExpression = simpleRegularExpression;
+exports.possibleElisions = possibleElisions;
+const v6 = __importStar(require("./constants"));
+function groupPossibilities(possibilities) {
+ return `(${possibilities.join('|')})`;
+}
+function padGroup(group) {
+ if (group.length < 4) {
+ return `0{0,${4 - group.length}}${group}`;
+ }
+ return group;
+}
+exports.ADDRESS_BOUNDARY = '[^A-Fa-f0-9:]';
+function simpleRegularExpression(groups) {
+ const zeroIndexes = [];
+ groups.forEach((group, i) => {
+ const groupInteger = parseInt(group, 16);
+ if (groupInteger === 0) {
+ zeroIndexes.push(i);
+ }
+ });
+ // You can technically elide a single 0, this creates the regular expressions
+ // to match that eventuality
+ const possibilities = zeroIndexes.map((zeroIndex) => groups
+ .map((group, i) => {
+ if (i === zeroIndex) {
+ const elision = i === 0 || i === v6.GROUPS - 1 ? ':' : '';
+ return groupPossibilities([padGroup(group), elision]);
+ }
+ return padGroup(group);
+ })
+ .join(':'));
+ // The simplest case
+ possibilities.push(groups.map(padGroup).join(':'));
+ return groupPossibilities(possibilities);
+}
+function possibleElisions(elidedGroups, moreLeft, moreRight) {
+ const left = moreLeft ? '' : ':';
+ const right = moreRight ? '' : ':';
+ const possibilities = [];
+ // 1. elision of everything (::)
+ if (!moreLeft && !moreRight) {
+ possibilities.push('::');
+ }
+ // 2. complete elision of the middle
+ if (moreLeft && moreRight) {
+ possibilities.push('');
+ }
+ if ((moreRight && !moreLeft) || (!moreRight && moreLeft)) {
+ // 3. complete elision of one side
+ possibilities.push(':');
+ }
+ // 4. elision from the left side
+ possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`);
+ // 5. elision from the right side
+ possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`);
+ // 6. no elision
+ possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`);
+ // 7. elision (including sloppy elision) from the middle
+ for (let groups = 1; groups < elidedGroups - 1; groups++) {
+ for (let position = 1; position < elidedGroups - groups; position++) {
+ possibilities.push(`(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`);
+ }
+ }
+ return groupPossibilities(possibilities);
+}
+//# sourceMappingURL=regular-expressions.js.map
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.js.map b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.js.map
new file mode 100644
index 000000000..190fd4a17
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/dist/v6/regular-expressions.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"regular-expressions.js","sourceRoot":"","sources":["../../src/v6/regular-expressions.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,gDAEC;AAED,4BAMC;AAID,0DA+BC;AAED,4CA4CC;AA7FD,gDAAkC;AAElC,SAAgB,kBAAkB,CAAC,aAAuB;IACxD,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACxC,CAAC;AAED,SAAgB,QAAQ,CAAC,KAAa;IACpC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAEY,QAAA,gBAAgB,GAAG,eAAe,CAAC;AAEhD,SAAgB,uBAAuB,CAAC,MAAgB;IACtD,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEzC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;YACvB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,4BAA4B;IAC5B,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAClD,MAAM;SACH,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAChB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAE1D,OAAO,kBAAkB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CACb,CAAC;IAEF,oBAAoB;IACpB,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAEnD,OAAO,kBAAkB,CAAC,aAAa,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,gBAAgB,CAC9B,YAAoB,EACpB,QAAkB,EAClB,SAAmB;IAEnB,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACjC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IAEnC,MAAM,aAAa,GAAG,EAAE,CAAC;IAEzB,gCAAgC;IAChC,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,oCAAoC;IACpC,IAAI,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC1B,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,QAAQ,CAAC,EAAE,CAAC;QACzD,kCAAkC;QAClC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,gCAAgC;IAChC,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,eAAe,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC;IAE9D,iCAAiC;IACjC,aAAa,CAAC,IAAI,CAAC,eAAe,YAAY,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAE/D,gBAAgB;IAChB,aAAa,CAAC,IAAI,CAAC,aAAa,YAAY,GAAG,CAAC,SAAS,CAAC,CAAC;IAE3D,wDAAwD;IACxD,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;QACzD,KAAK,IAAI,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC;YACpE,aAAa,CAAC,IAAI,CAChB,aAAa,QAAQ,eAAe,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,CAAC,SAAS,CAClF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,kBAAkB,CAAC,aAAa,CAAC,CAAC;AAC3C,CAAC"}
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/package.json
new file mode 100644
index 000000000..5cf811e8c
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/package.json
@@ -0,0 +1,78 @@
+{
+ "name": "ip-address",
+ "description": "A library for parsing IPv4 and IPv6 IP addresses in node and the browser.",
+ "keywords": [
+ "ipv6",
+ "ipv4",
+ "browser",
+ "validation"
+ ],
+ "version": "10.1.0",
+ "author": "Beau Gunderson (https://beaugunderson.com/)",
+ "license": "MIT",
+ "main": "dist/ip-address.js",
+ "types": "dist/ip-address.d.ts",
+ "scripts": {
+ "docs": "documentation build --github --output docs --format html ./ip-address.js",
+ "build": "rm -rf dist; mkdir dist; tsc",
+ "prepack": "npm run build",
+ "release": "release-it",
+ "test-ci": "nyc mocha",
+ "test": "mocha",
+ "watch": "mocha --watch"
+ },
+ "nyc": {
+ "extension": [
+ ".ts"
+ ],
+ "exclude": [
+ "**/*.d.ts",
+ ".eslintrc.js",
+ "coverage/",
+ "dist/",
+ "test/",
+ "tmp/"
+ ],
+ "reporter": [
+ "html",
+ "lcov",
+ "text"
+ ],
+ "all": true
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "files": [
+ "src",
+ "dist"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/beaugunderson/ip-address.git"
+ },
+ "devDependencies": {
+ "@types/chai": "^5.0.0",
+ "@types/mocha": "^10.0.8",
+ "@typescript-eslint/eslint-plugin": "^8.8.0",
+ "@typescript-eslint/parser": "^8.8.0",
+ "chai": "^5.1.1",
+ "documentation": "^14.0.3",
+ "eslint": "^8.50.0",
+ "eslint_d": "^14.0.4",
+ "eslint-config-airbnb": "^19.0.4",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-filenames": "^1.3.2",
+ "eslint-plugin-import": "^2.30.0",
+ "eslint-plugin-jsx-a11y": "^6.10.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
+ "mocha": "^10.7.3",
+ "nyc": "^17.1.0",
+ "prettier": "^3.3.3",
+ "release-it": "^17.6.0",
+ "source-map-support": "^0.5.21",
+ "tsx": "^4.19.1",
+ "typescript": "<5.6.0"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/address-error.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/address-error.ts
new file mode 100644
index 000000000..032d4cdd7
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/address-error.ts
@@ -0,0 +1,11 @@
+export class AddressError extends Error {
+ parseMessage?: string;
+
+ constructor(message: string, parseMessage?: string) {
+ super(message);
+
+ this.name = 'AddressError';
+
+ this.parseMessage = parseMessage;
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/common.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/common.ts
new file mode 100644
index 000000000..1123fa3b1
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/common.ts
@@ -0,0 +1,55 @@
+import { Address4 } from './ipv4';
+import { Address6 } from './ipv6';
+
+export interface ReverseFormOptions {
+ omitSuffix?: boolean;
+}
+
+export function isInSubnet(this: Address4 | Address6, address: Address4 | Address6) {
+ if (this.subnetMask < address.subnetMask) {
+ return false;
+ }
+
+ if (this.mask(address.subnetMask) === address.mask()) {
+ return true;
+ }
+
+ return false;
+}
+
+export function isCorrect(defaultBits: number) {
+ return function (this: Address4 | Address6) {
+ if (this.addressMinusSuffix !== this.correctForm()) {
+ return false;
+ }
+
+ if (this.subnetMask === defaultBits && !this.parsedSubnet) {
+ return true;
+ }
+
+ return this.parsedSubnet === String(this.subnetMask);
+ };
+}
+
+export function numberToPaddedHex(number: number) {
+ return number.toString(16).padStart(2, '0');
+}
+
+export function stringToPaddedHex(numberString: string) {
+ return numberToPaddedHex(parseInt(numberString, 10));
+}
+
+/**
+ * @param binaryValue Binary representation of a value (e.g. `10`)
+ * @param position Byte position, where 0 is the least significant bit
+ */
+export function testBit(binaryValue: string, position: number): boolean {
+ const { length } = binaryValue;
+
+ if (position > length) {
+ return false;
+ }
+
+ const positionInString = length - position;
+ return binaryValue.substring(positionInString, positionInString + 1) === '1';
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ip-address.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ip-address.ts
new file mode 100644
index 000000000..b4e150267
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ip-address.ts
@@ -0,0 +1,7 @@
+export { Address4 } from './ipv4';
+export { Address6 } from './ipv6';
+export { AddressError } from './address-error';
+
+import * as helpers from './v6/helpers';
+
+export const v6 = { helpers };
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ipv4.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ipv4.ts
new file mode 100644
index 000000000..7c7ce308b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ipv4.ts
@@ -0,0 +1,394 @@
+/* eslint-disable no-param-reassign */
+
+import * as common from './common';
+import * as constants from './v4/constants';
+import { AddressError } from './address-error';
+
+/**
+ * Represents an IPv4 address
+ * @class Address4
+ * @param {string} address - An IPv4 address string
+ */
+export class Address4 {
+ address: string;
+ addressMinusSuffix?: string;
+ groups: number = constants.GROUPS;
+ parsedAddress: string[] = [];
+ parsedSubnet: string = '';
+ subnet: string = '/32';
+ subnetMask: number = 32;
+ v4: boolean = true;
+
+ constructor(address: string) {
+ this.address = address;
+
+ const subnet = constants.RE_SUBNET_STRING.exec(address);
+
+ if (subnet) {
+ this.parsedSubnet = subnet[0].replace('/', '');
+ this.subnetMask = parseInt(this.parsedSubnet, 10);
+ this.subnet = `/${this.subnetMask}`;
+
+ if (this.subnetMask < 0 || this.subnetMask > constants.BITS) {
+ throw new AddressError('Invalid subnet mask.');
+ }
+
+ address = address.replace(constants.RE_SUBNET_STRING, '');
+ }
+
+ this.addressMinusSuffix = address;
+
+ this.parsedAddress = this.parse(address);
+ }
+
+ static isValid(address: string): boolean {
+ try {
+ // eslint-disable-next-line no-new
+ new Address4(address);
+
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ /*
+ * Parses a v4 address
+ */
+ parse(address: string) {
+ const groups = address.split('.');
+
+ if (!address.match(constants.RE_ADDRESS)) {
+ throw new AddressError('Invalid IPv4 address.');
+ }
+
+ return groups;
+ }
+
+ /**
+ * Returns the correct form of an address
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ correctForm(): string {
+ return this.parsedAddress.map((part) => parseInt(part, 10)).join('.');
+ }
+
+ /**
+ * Returns true if the address is correct, false otherwise
+ * @memberof Address4
+ * @instance
+ * @returns {Boolean}
+ */
+ isCorrect = common.isCorrect(constants.BITS);
+
+ /**
+ * Converts a hex string to an IPv4 address object
+ * @memberof Address4
+ * @static
+ * @param {string} hex - a hex string to convert
+ * @returns {Address4}
+ */
+ static fromHex(hex: string): Address4 {
+ const padded = hex.replace(/:/g, '').padStart(8, '0');
+ const groups = [];
+ let i;
+
+ for (i = 0; i < 8; i += 2) {
+ const h = padded.slice(i, i + 2);
+
+ groups.push(parseInt(h, 16));
+ }
+
+ return new Address4(groups.join('.'));
+ }
+
+ /**
+ * Converts an integer into a IPv4 address object
+ * @memberof Address4
+ * @static
+ * @param {integer} integer - a number to convert
+ * @returns {Address4}
+ */
+ static fromInteger(integer: number): Address4 {
+ return Address4.fromHex(integer.toString(16));
+ }
+
+ /**
+ * Return an address from in-addr.arpa form
+ * @memberof Address4
+ * @static
+ * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
+ * @returns {Adress4}
+ * @example
+ * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
+ * address.correctForm(); // '192.0.2.42'
+ */
+ static fromArpa(arpaFormAddress: string): Address4 {
+ // remove ending ".in-addr.arpa." or just "."
+ const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, '');
+
+ const address = leader.split('.').reverse().join('.');
+
+ return new Address4(address);
+ }
+
+ /**
+ * Converts an IPv4 address object to a hex string
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ toHex(): string {
+ return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':');
+ }
+
+ /**
+ * Converts an IPv4 address object to an array of bytes
+ * @memberof Address4
+ * @instance
+ * @returns {Array}
+ */
+ toArray(): number[] {
+ return this.parsedAddress.map((part) => parseInt(part, 10));
+ }
+
+ /**
+ * Converts an IPv4 address object to an IPv6 address group
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ toGroup6(): string {
+ const output = [];
+ let i;
+
+ for (i = 0; i < constants.GROUPS; i += 2) {
+ output.push(
+ `${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(
+ this.parsedAddress[i + 1],
+ )}`,
+ );
+ }
+
+ return output.join(':');
+ }
+
+ /**
+ * Returns the address as a `bigint`
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ bigInt(): bigint {
+ return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join('')}`);
+ }
+
+ /**
+ * Helper function getting start address.
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ _startAddress(): bigint {
+ return BigInt(`0b${this.mask() + '0'.repeat(constants.BITS - this.subnetMask)}`);
+ }
+
+ /**
+ * The first address in the range given by this address' subnet.
+ * Often referred to as the Network Address.
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ startAddress(): Address4 {
+ return Address4.fromBigInt(this._startAddress());
+ }
+
+ /**
+ * The first host address in the range given by this address's subnet ie
+ * the first address after the Network Address
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ startAddressExclusive(): Address4 {
+ const adjust = BigInt('1');
+ return Address4.fromBigInt(this._startAddress() + adjust);
+ }
+
+ /**
+ * Helper function getting end address.
+ * @memberof Address4
+ * @instance
+ * @returns {bigint}
+ */
+ _endAddress(): bigint {
+ return BigInt(`0b${this.mask() + '1'.repeat(constants.BITS - this.subnetMask)}`);
+ }
+
+ /**
+ * The last address in the range given by this address' subnet
+ * Often referred to as the Broadcast
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ endAddress(): Address4 {
+ return Address4.fromBigInt(this._endAddress());
+ }
+
+ /**
+ * The last host address in the range given by this address's subnet ie
+ * the last address prior to the Broadcast Address
+ * @memberof Address4
+ * @instance
+ * @returns {Address4}
+ */
+ endAddressExclusive(): Address4 {
+ const adjust = BigInt('1');
+ return Address4.fromBigInt(this._endAddress() - adjust);
+ }
+
+ /**
+ * Converts a BigInt to a v4 address object
+ * @memberof Address4
+ * @static
+ * @param {bigint} bigInt - a BigInt to convert
+ * @returns {Address4}
+ */
+ static fromBigInt(bigInt: bigint): Address4 {
+ return Address4.fromHex(bigInt.toString(16));
+ }
+
+ /**
+ * Convert a byte array to an Address4 object
+ * @memberof Address4
+ * @static
+ * @param {Array} bytes - an array of 4 bytes (0-255)
+ * @returns {Address4}
+ */
+ static fromByteArray(bytes: Array): Address4 {
+ if (bytes.length !== 4) {
+ throw new AddressError('IPv4 addresses require exactly 4 bytes');
+ }
+
+ // Validate that all bytes are within valid range (0-255)
+ for (let i = 0; i < bytes.length; i++) {
+ if (!Number.isInteger(bytes[i]) || bytes[i] < 0 || bytes[i] > 255) {
+ throw new AddressError('All bytes must be integers between 0 and 255');
+ }
+ }
+
+ return this.fromUnsignedByteArray(bytes);
+ }
+
+ /**
+ * Convert an unsigned byte array to an Address4 object
+ * @memberof Address4
+ * @static
+ * @param {Array} bytes - an array of 4 unsigned bytes (0-255)
+ * @returns {Address4}
+ */
+ static fromUnsignedByteArray(bytes: Array): Address4 {
+ if (bytes.length !== 4) {
+ throw new AddressError('IPv4 addresses require exactly 4 bytes');
+ }
+
+ const address = bytes.join('.');
+ return new Address4(address);
+ }
+
+ /**
+ * Returns the first n bits of the address, defaulting to the
+ * subnet mask
+ * @memberof Address4
+ * @instance
+ * @returns {String}
+ */
+ mask(mask?: number): string {
+ if (mask === undefined) {
+ mask = this.subnetMask;
+ }
+
+ return this.getBitsBase2(0, mask);
+ }
+
+ /**
+ * Returns the bits in the given range as a base-2 string
+ * @memberof Address4
+ * @instance
+ * @returns {string}
+ */
+ getBitsBase2(start: number, end: number): string {
+ return this.binaryZeroPad().slice(start, end);
+ }
+
+ /**
+ * Return the reversed ip6.arpa form of the address
+ * @memberof Address4
+ * @param {Object} options
+ * @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
+ * @instance
+ * @returns {String}
+ */
+ reverseForm(options?: common.ReverseFormOptions): string {
+ if (!options) {
+ options = {};
+ }
+
+ const reversed = this.correctForm().split('.').reverse().join('.');
+
+ if (options.omitSuffix) {
+ return reversed;
+ }
+
+ return `${reversed}.in-addr.arpa.`;
+ }
+
+ /**
+ * Returns true if the given address is in the subnet of the current address
+ * @memberof Address4
+ * @instance
+ * @returns {boolean}
+ */
+ isInSubnet = common.isInSubnet;
+
+ /**
+ * Returns true if the given address is a multicast address
+ * @memberof Address4
+ * @instance
+ * @returns {boolean}
+ */
+ isMulticast(): boolean {
+ return this.isInSubnet(new Address4('224.0.0.0/4'));
+ }
+
+ /**
+ * Returns a zero-padded base-2 string representation of the address
+ * @memberof Address4
+ * @instance
+ * @returns {string}
+ */
+ binaryZeroPad(): string {
+ return this.bigInt().toString(2).padStart(constants.BITS, '0');
+ }
+
+ /**
+ * Groups an IPv4 address for inclusion at the end of an IPv6 address
+ * @returns {String}
+ */
+ groupForV6(): string {
+ const segments = this.parsedAddress;
+
+ return this.address.replace(
+ constants.RE_ADDRESS,
+ `${segments
+ .slice(0, 2)
+ .join('.')} .${segments
+ .slice(2, 4)
+ .join('.')} `,
+ );
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ipv6.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ipv6.ts
new file mode 100644
index 000000000..60348e97a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/ipv6.ts
@@ -0,0 +1,1212 @@
+/* eslint-disable prefer-destructuring */
+/* eslint-disable no-param-reassign */
+
+import * as common from './common';
+import * as constants4 from './v4/constants';
+import * as constants6 from './v6/constants';
+import * as helpers from './v6/helpers';
+import { Address4 } from './ipv4';
+import {
+ ADDRESS_BOUNDARY,
+ possibleElisions,
+ simpleRegularExpression,
+} from './v6/regular-expressions';
+import { AddressError } from './address-error';
+import { testBit } from './common';
+
+function assert(condition: any): asserts condition {
+ if (!condition) {
+ throw new Error('Assertion failed.');
+ }
+}
+
+function addCommas(number: string): string {
+ const r = /(\d+)(\d{3})/;
+
+ while (r.test(number)) {
+ number = number.replace(r, '$1,$2');
+ }
+
+ return number;
+}
+
+function spanLeadingZeroes4(n: string): string {
+ n = n.replace(/^(0{1,})([1-9]+)$/, '$1 $2');
+ n = n.replace(/^(0{1,})(0)$/, '$1 $2');
+
+ return n;
+}
+
+/*
+ * A helper function to compact an array
+ */
+function compact(address: string[], slice: number[]) {
+ const s1 = [];
+ const s2 = [];
+ let i;
+
+ for (i = 0; i < address.length; i++) {
+ if (i < slice[0]) {
+ s1.push(address[i]);
+ } else if (i > slice[1]) {
+ s2.push(address[i]);
+ }
+ }
+
+ return s1.concat(['compact']).concat(s2);
+}
+
+function paddedHex(octet: string): string {
+ return parseInt(octet, 16).toString(16).padStart(4, '0');
+}
+
+function unsignByte(b: number) {
+ // eslint-disable-next-line no-bitwise
+ return b & 0xff;
+}
+
+interface SixToFourProperties {
+ prefix: string;
+ gateway: string;
+}
+
+interface TeredoProperties {
+ prefix: string;
+ server4: string;
+ client4: string;
+ flags: string;
+ coneNat: boolean;
+ microsoft: {
+ reserved: boolean;
+ universalLocal: boolean;
+ groupIndividual: boolean;
+ nonce: string;
+ };
+ udpPort: string;
+}
+
+/**
+ * Represents an IPv6 address
+ * @class Address6
+ * @param {string} address - An IPv6 address string
+ * @param {number} [groups=8] - How many octets to parse
+ * @example
+ * var address = new Address6('2001::/32');
+ */
+export class Address6 {
+ address4?: Address4;
+ address: string;
+ addressMinusSuffix: string = '';
+ elidedGroups?: number;
+ elisionBegin?: number;
+ elisionEnd?: number;
+ groups: number;
+ parsedAddress4?: string;
+ parsedAddress: string[];
+ parsedSubnet: string = '';
+ subnet: string = '/128';
+ subnetMask: number = 128;
+ v4: boolean = false;
+ zone: string = '';
+
+ constructor(address: string, optionalGroups?: number) {
+ if (optionalGroups === undefined) {
+ this.groups = constants6.GROUPS;
+ } else {
+ this.groups = optionalGroups;
+ }
+
+ this.address = address;
+
+ const subnet = constants6.RE_SUBNET_STRING.exec(address);
+
+ if (subnet) {
+ this.parsedSubnet = subnet[0].replace('/', '');
+ this.subnetMask = parseInt(this.parsedSubnet, 10);
+ this.subnet = `/${this.subnetMask}`;
+
+ if (
+ Number.isNaN(this.subnetMask) ||
+ this.subnetMask < 0 ||
+ this.subnetMask > constants6.BITS
+ ) {
+ throw new AddressError('Invalid subnet mask.');
+ }
+
+ address = address.replace(constants6.RE_SUBNET_STRING, '');
+ } else if (/\//.test(address)) {
+ throw new AddressError('Invalid subnet mask.');
+ }
+
+ const zone = constants6.RE_ZONE_STRING.exec(address);
+
+ if (zone) {
+ this.zone = zone[0];
+
+ address = address.replace(constants6.RE_ZONE_STRING, '');
+ }
+
+ this.addressMinusSuffix = address;
+
+ this.parsedAddress = this.parse(this.addressMinusSuffix);
+ }
+
+ static isValid(address: string): boolean {
+ try {
+ // eslint-disable-next-line no-new
+ new Address6(address);
+
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ /**
+ * Convert a BigInt to a v6 address object
+ * @memberof Address6
+ * @static
+ * @param {bigint} bigInt - a BigInt to convert
+ * @returns {Address6}
+ * @example
+ * var bigInt = BigInt('1000000000000');
+ * var address = Address6.fromBigInt(bigInt);
+ * address.correctForm(); // '::e8:d4a5:1000'
+ */
+ static fromBigInt(bigInt: bigint): Address6 {
+ const hex = bigInt.toString(16).padStart(32, '0');
+ const groups = [];
+ let i;
+
+ for (i = 0; i < constants6.GROUPS; i++) {
+ groups.push(hex.slice(i * 4, (i + 1) * 4));
+ }
+
+ return new Address6(groups.join(':'));
+ }
+
+ /**
+ * Convert a URL (with optional port number) to an address object
+ * @memberof Address6
+ * @static
+ * @param {string} url - a URL with optional port number
+ * @example
+ * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');
+ * addressAndPort.address.correctForm(); // 'ffff::'
+ * addressAndPort.port; // 8080
+ */
+ static fromURL(url: string) {
+ let host: string;
+ let port: string | number | null = null;
+ let result: string[] | null;
+
+ // If we have brackets parse them and find a port
+ if (url.indexOf('[') !== -1 && url.indexOf(']:') !== -1) {
+ result = constants6.RE_URL_WITH_PORT.exec(url);
+
+ if (result === null) {
+ return {
+ error: 'failed to parse address with port',
+ address: null,
+ port: null,
+ };
+ }
+
+ host = result[1];
+ port = result[2];
+ // If there's a URL extract the address
+ } else if (url.indexOf('/') !== -1) {
+ // Remove the protocol prefix
+ url = url.replace(/^[a-z0-9]+:\/\//, '');
+
+ // Parse the address
+ result = constants6.RE_URL.exec(url);
+
+ if (result === null) {
+ return {
+ error: 'failed to parse address from URL',
+ address: null,
+ port: null,
+ };
+ }
+
+ host = result[1];
+ // Otherwise just assign the URL to the host and let the library parse it
+ } else {
+ host = url;
+ }
+
+ // If there's a port convert it to an integer
+ if (port) {
+ port = parseInt(port, 10);
+
+ // squelch out of range ports
+ if (port < 0 || port > 65536) {
+ port = null;
+ }
+ } else {
+ // Standardize `undefined` to `null`
+ port = null;
+ }
+
+ return {
+ address: new Address6(host),
+ port,
+ };
+ }
+
+ /**
+ * Create an IPv6-mapped address given an IPv4 address
+ * @memberof Address6
+ * @static
+ * @param {string} address - An IPv4 address string
+ * @returns {Address6}
+ * @example
+ * var address = Address6.fromAddress4('192.168.0.1');
+ * address.correctForm(); // '::ffff:c0a8:1'
+ * address.to4in6(); // '::ffff:192.168.0.1'
+ */
+ static fromAddress4(address: string): Address6 {
+ const address4 = new Address4(address);
+
+ const mask6 = constants6.BITS - (constants4.BITS - address4.subnetMask);
+
+ return new Address6(`::ffff:${address4.correctForm()}/${mask6}`);
+ }
+
+ /**
+ * Return an address from ip6.arpa form
+ * @memberof Address6
+ * @static
+ * @param {string} arpaFormAddress - an 'ip6.arpa' form address
+ * @returns {Adress6}
+ * @example
+ * var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)
+ * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'
+ */
+ static fromArpa(arpaFormAddress: string): Address6 {
+ // remove ending ".ip6.arpa." or just "."
+ let address = arpaFormAddress.replace(/(\.ip6\.arpa)?\.$/, '');
+ const semicolonAmount = 7;
+
+ // correct ip6.arpa form with ending removed will be 63 characters
+ if (address.length !== 63) {
+ throw new AddressError("Invalid 'ip6.arpa' form.");
+ }
+
+ const parts = address.split('.').reverse();
+
+ for (let i = semicolonAmount; i > 0; i--) {
+ const insertIndex = i * 4;
+ parts.splice(insertIndex, 0, ':');
+ }
+
+ address = parts.join('');
+
+ return new Address6(address);
+ }
+
+ /**
+ * Return the Microsoft UNC transcription of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String} the Microsoft UNC transcription of the address
+ */
+ microsoftTranscription(): string {
+ return `${this.correctForm().replace(/:/g, '-')}.ipv6-literal.net`;
+ }
+
+ /**
+ * Return the first n bits of the address, defaulting to the subnet mask
+ * @memberof Address6
+ * @instance
+ * @param {number} [mask=subnet] - the number of bits to mask
+ * @returns {String} the first n bits of the address as a string
+ */
+ mask(mask: number = this.subnetMask): string {
+ return this.getBitsBase2(0, mask);
+ }
+
+ /**
+ * Return the number of possible subnets of a given size in the address
+ * @memberof Address6
+ * @instance
+ * @param {number} [subnetSize=128] - the subnet size
+ * @returns {String}
+ */
+ // TODO: probably useful to have a numeric version of this too
+ possibleSubnets(subnetSize: number = 128): string {
+ const availableBits = constants6.BITS - this.subnetMask;
+ const subnetBits = Math.abs(subnetSize - constants6.BITS);
+ const subnetPowers = availableBits - subnetBits;
+
+ if (subnetPowers < 0) {
+ return '0';
+ }
+
+ return addCommas((BigInt('2') ** BigInt(subnetPowers)).toString(10));
+ }
+
+ /**
+ * Helper function getting start address.
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ _startAddress(): bigint {
+ return BigInt(`0b${this.mask() + '0'.repeat(constants6.BITS - this.subnetMask)}`);
+ }
+
+ /**
+ * The first address in the range given by this address' subnet
+ * Often referred to as the Network Address.
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ startAddress(): Address6 {
+ return Address6.fromBigInt(this._startAddress());
+ }
+
+ /**
+ * The first host address in the range given by this address's subnet ie
+ * the first address after the Network Address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ startAddressExclusive(): Address6 {
+ const adjust = BigInt('1');
+ return Address6.fromBigInt(this._startAddress() + adjust);
+ }
+
+ /**
+ * Helper function getting end address.
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ _endAddress(): bigint {
+ return BigInt(`0b${this.mask() + '1'.repeat(constants6.BITS - this.subnetMask)}`);
+ }
+
+ /**
+ * The last address in the range given by this address' subnet
+ * Often referred to as the Broadcast
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ endAddress(): Address6 {
+ return Address6.fromBigInt(this._endAddress());
+ }
+
+ /**
+ * The last host address in the range given by this address's subnet ie
+ * the last address prior to the Broadcast Address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ endAddressExclusive(): Address6 {
+ const adjust = BigInt('1');
+ return Address6.fromBigInt(this._endAddress() - adjust);
+ }
+
+ /**
+ * Return the scope of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getScope(): string {
+ let scope = constants6.SCOPES[parseInt(this.getBits(12, 16).toString(10), 10)];
+
+ if (this.getType() === 'Global unicast' && scope !== 'Link local') {
+ scope = 'Global';
+ }
+
+ return scope || 'Unknown';
+ }
+
+ /**
+ * Return the type of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getType(): string {
+ for (const subnet of Object.keys(constants6.TYPES)) {
+ if (this.isInSubnet(new Address6(subnet))) {
+ return constants6.TYPES[subnet] as string;
+ }
+ }
+
+ return 'Global unicast';
+ }
+
+ /**
+ * Return the bits in the given range as a BigInt
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ getBits(start: number, end: number): bigint {
+ return BigInt(`0b${this.getBitsBase2(start, end)}`);
+ }
+
+ /**
+ * Return the bits in the given range as a base-2 string
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsBase2(start: number, end: number): string {
+ return this.binaryZeroPad().slice(start, end);
+ }
+
+ /**
+ * Return the bits in the given range as a base-16 string
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsBase16(start: number, end: number): string {
+ const length = end - start;
+
+ if (length % 4 !== 0) {
+ throw new Error('Length of bits to retrieve must be divisible by four');
+ }
+
+ return this.getBits(start, end)
+ .toString(16)
+ .padStart(length / 4, '0');
+ }
+
+ /**
+ * Return the bits that are set past the subnet mask length
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ getBitsPastSubnet(): string {
+ return this.getBitsBase2(this.subnetMask, constants6.BITS);
+ }
+
+ /**
+ * Return the reversed ip6.arpa form of the address
+ * @memberof Address6
+ * @param {Object} options
+ * @param {boolean} options.omitSuffix - omit the "ip6.arpa" suffix
+ * @instance
+ * @returns {String}
+ */
+ reverseForm(options?: common.ReverseFormOptions): string {
+ if (!options) {
+ options = {};
+ }
+
+ const characters = Math.floor(this.subnetMask / 4);
+
+ const reversed = this.canonicalForm()
+ .replace(/:/g, '')
+ .split('')
+ .slice(0, characters)
+ .reverse()
+ .join('.');
+
+ if (characters > 0) {
+ if (options.omitSuffix) {
+ return reversed;
+ }
+
+ return `${reversed}.ip6.arpa.`;
+ }
+
+ if (options.omitSuffix) {
+ return '';
+ }
+
+ return 'ip6.arpa.';
+ }
+
+ /**
+ * Return the correct form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ correctForm(): string {
+ let i;
+ let groups = [];
+
+ let zeroCounter = 0;
+ const zeroes = [];
+
+ for (i = 0; i < this.parsedAddress.length; i++) {
+ const value = parseInt(this.parsedAddress[i], 16);
+
+ if (value === 0) {
+ zeroCounter++;
+ }
+
+ if (value !== 0 && zeroCounter > 0) {
+ if (zeroCounter > 1) {
+ zeroes.push([i - zeroCounter, i - 1]);
+ }
+
+ zeroCounter = 0;
+ }
+ }
+
+ // Do we end with a string of zeroes?
+ if (zeroCounter > 1) {
+ zeroes.push([this.parsedAddress.length - zeroCounter, this.parsedAddress.length - 1]);
+ }
+
+ const zeroLengths = zeroes.map((n) => n[1] - n[0] + 1);
+
+ if (zeroes.length > 0) {
+ const index = zeroLengths.indexOf(Math.max(...zeroLengths) as number);
+
+ groups = compact(this.parsedAddress, zeroes[index]);
+ } else {
+ groups = this.parsedAddress;
+ }
+
+ for (i = 0; i < groups.length; i++) {
+ if (groups[i] !== 'compact') {
+ groups[i] = parseInt(groups[i], 16).toString(16);
+ }
+ }
+
+ let correct = groups.join(':');
+
+ correct = correct.replace(/^compact$/, '::');
+ correct = correct.replace(/(^compact)|(compact$)/, ':');
+ correct = correct.replace(/compact/, '');
+
+ return correct;
+ }
+
+ /**
+ * Return a zero-padded base-2 string representation of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ * @example
+ * var address = new Address6('2001:4860:4001:803::1011');
+ * address.binaryZeroPad();
+ * // '0010000000000001010010000110000001000000000000010000100000000011
+ * // 0000000000000000000000000000000000000000000000000001000000010001'
+ */
+ binaryZeroPad(): string {
+ return this.bigInt().toString(2).padStart(constants6.BITS, '0');
+ }
+
+ // TODO: Improve the semantics of this helper function
+ parse4in6(address: string): string {
+ const groups = address.split(':');
+ const lastGroup = groups.slice(-1)[0];
+
+ const address4 = lastGroup.match(constants4.RE_ADDRESS);
+
+ if (address4) {
+ this.parsedAddress4 = address4[0];
+ this.address4 = new Address4(this.parsedAddress4);
+
+ for (let i = 0; i < this.address4.groups; i++) {
+ if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) {
+ throw new AddressError(
+ "IPv4 addresses can't have leading zeroes.",
+ address.replace(
+ constants4.RE_ADDRESS,
+ this.address4.parsedAddress.map(spanLeadingZeroes4).join('.'),
+ ),
+ );
+ }
+ }
+
+ this.v4 = true;
+
+ groups[groups.length - 1] = this.address4.toGroup6();
+
+ address = groups.join(':');
+ }
+
+ return address;
+ }
+
+ // TODO: Make private?
+ parse(address: string): string[] {
+ address = this.parse4in6(address);
+
+ const badCharacters = address.match(constants6.RE_BAD_CHARACTERS);
+
+ if (badCharacters) {
+ throw new AddressError(
+ `Bad character${
+ badCharacters.length > 1 ? 's' : ''
+ } detected in address: ${badCharacters.join('')}`,
+ address.replace(constants6.RE_BAD_CHARACTERS, '$1 '),
+ );
+ }
+
+ const badAddress = address.match(constants6.RE_BAD_ADDRESS);
+
+ if (badAddress) {
+ throw new AddressError(
+ `Address failed regex: ${badAddress.join('')}`,
+ address.replace(constants6.RE_BAD_ADDRESS, '$1 '),
+ );
+ }
+
+ let groups: string[] = [];
+
+ const halves = address.split('::');
+
+ if (halves.length === 2) {
+ let first = halves[0].split(':');
+ let last = halves[1].split(':');
+
+ if (first.length === 1 && first[0] === '') {
+ first = [];
+ }
+
+ if (last.length === 1 && last[0] === '') {
+ last = [];
+ }
+
+ const remaining = this.groups - (first.length + last.length);
+
+ if (!remaining) {
+ throw new AddressError('Error parsing groups');
+ }
+
+ this.elidedGroups = remaining;
+
+ this.elisionBegin = first.length;
+ this.elisionEnd = first.length + this.elidedGroups;
+
+ groups = groups.concat(first);
+
+ for (let i = 0; i < remaining; i++) {
+ groups.push('0');
+ }
+
+ groups = groups.concat(last);
+ } else if (halves.length === 1) {
+ groups = address.split(':');
+
+ this.elidedGroups = 0;
+ } else {
+ throw new AddressError('Too many :: groups found');
+ }
+
+ groups = groups.map((group: string) => parseInt(group, 16).toString(16));
+
+ if (groups.length !== this.groups) {
+ throw new AddressError('Incorrect number of groups found');
+ }
+
+ return groups;
+ }
+
+ /**
+ * Return the canonical form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ canonicalForm(): string {
+ return this.parsedAddress.map(paddedHex).join(':');
+ }
+
+ /**
+ * Return the decimal form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ decimal(): string {
+ return this.parsedAddress.map((n) => parseInt(n, 16).toString(10).padStart(5, '0')).join(':');
+ }
+
+ /**
+ * Return the address as a BigInt
+ * @memberof Address6
+ * @instance
+ * @returns {bigint}
+ */
+ bigInt(): bigint {
+ return BigInt(`0x${this.parsedAddress.map(paddedHex).join('')}`);
+ }
+
+ /**
+ * Return the last two groups of this address as an IPv4 address string
+ * @memberof Address6
+ * @instance
+ * @returns {Address4}
+ * @example
+ * var address = new Address6('2001:4860:4001::1825:bf11');
+ * address.to4().correctForm(); // '24.37.191.17'
+ */
+ to4(): Address4 {
+ const binary = this.binaryZeroPad().split('');
+
+ return Address4.fromHex(BigInt(`0b${binary.slice(96, 128).join('')}`).toString(16));
+ }
+
+ /**
+ * Return the v4-in-v6 form of the address
+ * @memberof Address6
+ * @instance
+ * @returns {String}
+ */
+ to4in6(): string {
+ const address4 = this.to4();
+ const address6 = new Address6(this.parsedAddress.slice(0, 6).join(':'), 6);
+
+ const correct = address6.correctForm();
+
+ let infix = '';
+
+ if (!/:$/.test(correct)) {
+ infix = ':';
+ }
+
+ return correct + infix + address4.address;
+ }
+
+ /**
+ * Return an object containing the Teredo properties of the address
+ * @memberof Address6
+ * @instance
+ * @returns {Object}
+ */
+ inspectTeredo(): TeredoProperties {
+ /*
+ - Bits 0 to 31 are set to the Teredo prefix (normally 2001:0000::/32).
+ - Bits 32 to 63 embed the primary IPv4 address of the Teredo server that
+ is used.
+ - Bits 64 to 79 can be used to define some flags. Currently only the
+ higher order bit is used; it is set to 1 if the Teredo client is
+ located behind a cone NAT, 0 otherwise. For Microsoft's Windows Vista
+ and Windows Server 2008 implementations, more bits are used. In those
+ implementations, the format for these 16 bits is "CRAAAAUG AAAAAAAA",
+ where "C" remains the "Cone" flag. The "R" bit is reserved for future
+ use. The "U" bit is for the Universal/Local flag (set to 0). The "G" bit
+ is Individual/Group flag (set to 0). The A bits are set to a 12-bit
+ randomly generated number chosen by the Teredo client to introduce
+ additional protection for the Teredo node against IPv6-based scanning
+ attacks.
+ - Bits 80 to 95 contains the obfuscated UDP port number. This is the
+ port number that is mapped by the NAT to the Teredo client with all
+ bits inverted.
+ - Bits 96 to 127 contains the obfuscated IPv4 address. This is the
+ public IPv4 address of the NAT with all bits inverted.
+ */
+ const prefix = this.getBitsBase16(0, 32);
+
+ const bitsForUdpPort: bigint = this.getBits(80, 96);
+ // eslint-disable-next-line no-bitwise
+ const udpPort = (bitsForUdpPort ^ BigInt('0xffff')).toString();
+
+ const server4 = Address4.fromHex(this.getBitsBase16(32, 64));
+
+ const bitsForClient4 = this.getBits(96, 128);
+ // eslint-disable-next-line no-bitwise
+ const client4 = Address4.fromHex((bitsForClient4 ^ BigInt('0xffffffff')).toString(16));
+
+ const flagsBase2 = this.getBitsBase2(64, 80);
+
+ const coneNat = testBit(flagsBase2, 15);
+ const reserved = testBit(flagsBase2, 14);
+ const groupIndividual = testBit(flagsBase2, 8);
+ const universalLocal = testBit(flagsBase2, 9);
+ const nonce = BigInt(`0b${flagsBase2.slice(2, 6) + flagsBase2.slice(8, 16)}`).toString(10);
+
+ return {
+ prefix: `${prefix.slice(0, 4)}:${prefix.slice(4, 8)}`,
+ server4: server4.address,
+ client4: client4.address,
+ flags: flagsBase2,
+ coneNat,
+ microsoft: {
+ reserved,
+ universalLocal,
+ groupIndividual,
+ nonce,
+ },
+ udpPort,
+ };
+ }
+
+ /**
+ * Return an object containing the 6to4 properties of the address
+ * @memberof Address6
+ * @instance
+ * @returns {Object}
+ */
+ inspect6to4(): SixToFourProperties {
+ /*
+ - Bits 0 to 15 are set to the 6to4 prefix (2002::/16).
+ - Bits 16 to 48 embed the IPv4 address of the 6to4 gateway that is used.
+ */
+
+ const prefix = this.getBitsBase16(0, 16);
+
+ const gateway = Address4.fromHex(this.getBitsBase16(16, 48));
+
+ return {
+ prefix: prefix.slice(0, 4),
+ gateway: gateway.address,
+ };
+ }
+
+ /**
+ * Return a v6 6to4 address from a v6 v4inv6 address
+ * @memberof Address6
+ * @instance
+ * @returns {Address6}
+ */
+ to6to4(): Address6 | null {
+ if (!this.is4()) {
+ return null;
+ }
+
+ const addr6to4 = [
+ '2002',
+ this.getBitsBase16(96, 112),
+ this.getBitsBase16(112, 128),
+ '',
+ '/16',
+ ].join(':');
+
+ return new Address6(addr6to4);
+ }
+
+ /**
+ * Return a byte array
+ * @memberof Address6
+ * @instance
+ * @returns {Array}
+ */
+ toByteArray(): number[] {
+ const valueWithoutPadding = this.bigInt().toString(16);
+ const leadingPad = '0'.repeat(valueWithoutPadding.length % 2);
+
+ const value = `${leadingPad}${valueWithoutPadding}`;
+
+ const bytes = [];
+ for (let i = 0, length = value.length; i < length; i += 2) {
+ bytes.push(parseInt(value.substring(i, i + 2), 16));
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Return an unsigned byte array
+ * @memberof Address6
+ * @instance
+ * @returns {Array}
+ */
+ toUnsignedByteArray(): number[] {
+ return this.toByteArray().map(unsignByte);
+ }
+
+ /**
+ * Convert a byte array to an Address6 object
+ * @memberof Address6
+ * @static
+ * @returns {Address6}
+ */
+ static fromByteArray(bytes: Array): Address6 {
+ return this.fromUnsignedByteArray(bytes.map(unsignByte));
+ }
+
+ /**
+ * Convert an unsigned byte array to an Address6 object
+ * @memberof Address6
+ * @static
+ * @returns {Address6}
+ */
+ static fromUnsignedByteArray(bytes: Array): Address6 {
+ const BYTE_MAX = BigInt('256');
+ let result = BigInt('0');
+ let multiplier = BigInt('1');
+
+ for (let i = bytes.length - 1; i >= 0; i--) {
+ result += multiplier * BigInt(bytes[i].toString(10));
+
+ multiplier *= BYTE_MAX;
+ }
+
+ return Address6.fromBigInt(result);
+ }
+
+ // #region Attributes
+ /**
+ * Returns true if the given address is in the subnet of the current address
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isInSubnet = common.isInSubnet;
+
+ /**
+ * Returns true if the address is correct, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isCorrect = common.isCorrect(constants6.BITS);
+
+ /**
+ * Returns true if the address is in the canonical form, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isCanonical(): boolean {
+ return this.addressMinusSuffix === this.canonicalForm();
+ }
+
+ /**
+ * Returns true if the address is a link local address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isLinkLocal(): boolean {
+ // Zeroes are required, i.e. we can't check isInSubnet with 'fe80::/10'
+ if (
+ this.getBitsBase2(0, 64) ===
+ '1111111010000000000000000000000000000000000000000000000000000000'
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the address is a multicast address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isMulticast(): boolean {
+ return this.getType() === 'Multicast';
+ }
+
+ /**
+ * Returns true if the address is a v4-in-v6 address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ is4(): boolean {
+ return this.v4;
+ }
+
+ /**
+ * Returns true if the address is a Teredo address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isTeredo(): boolean {
+ return this.isInSubnet(new Address6('2001::/32'));
+ }
+
+ /**
+ * Returns true if the address is a 6to4 address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ is6to4(): boolean {
+ return this.isInSubnet(new Address6('2002::/16'));
+ }
+
+ /**
+ * Returns true if the address is a loopback address, false otherwise
+ * @memberof Address6
+ * @instance
+ * @returns {boolean}
+ */
+ isLoopback(): boolean {
+ return this.getType() === 'Loopback';
+ }
+ // #endregion
+
+ // #region HTML
+ /**
+ * @returns {String} the address in link form with a default port of 80
+ */
+ href(optionalPort?: number | string): string {
+ if (optionalPort === undefined) {
+ optionalPort = '';
+ } else {
+ optionalPort = `:${optionalPort}`;
+ }
+
+ return `http://[${this.correctForm()}]${optionalPort}/`;
+ }
+
+ /**
+ * @returns {String} a link suitable for conveying the address via a URL hash
+ */
+ link(options?: { className?: string; prefix?: string; v4?: boolean }): string {
+ if (!options) {
+ options = {};
+ }
+
+ if (options.className === undefined) {
+ options.className = '';
+ }
+
+ if (options.prefix === undefined) {
+ options.prefix = '/#address=';
+ }
+
+ if (options.v4 === undefined) {
+ options.v4 = false;
+ }
+
+ let formFunction = this.correctForm;
+
+ if (options.v4) {
+ formFunction = this.to4in6;
+ }
+
+ const form = formFunction.call(this);
+
+ if (options.className) {
+ return `${form} `;
+ }
+
+ return `${form} `;
+ }
+
+ /**
+ * Groups an address
+ * @returns {String}
+ */
+ group(): string {
+ if (this.elidedGroups === 0) {
+ // The simple case
+ return helpers.simpleGroup(this.address).join(':');
+ }
+
+ assert(typeof this.elidedGroups === 'number');
+ assert(typeof this.elisionBegin === 'number');
+
+ // The elided case
+ const output = [];
+
+ const [left, right] = this.address.split('::');
+
+ if (left.length) {
+ output.push(...helpers.simpleGroup(left));
+ } else {
+ output.push('');
+ }
+
+ const classes = ['hover-group'];
+
+ for (let i = this.elisionBegin; i < this.elisionBegin + this.elidedGroups; i++) {
+ classes.push(`group-${i}`);
+ }
+
+ output.push(` `);
+
+ if (right.length) {
+ output.push(...helpers.simpleGroup(right, this.elisionEnd));
+ } else {
+ output.push('');
+ }
+
+ if (this.is4()) {
+ assert(this.address4 instanceof Address4);
+
+ output.pop();
+ output.push(this.address4.groupForV6());
+ }
+
+ return output.join(':');
+ }
+ // #endregion
+
+ // #region Regular expressions
+ /**
+ * Generate a regular expression string that can be used to find or validate
+ * all variations of this address
+ * @memberof Address6
+ * @instance
+ * @param {boolean} substringSearch
+ * @returns {string}
+ */
+ regularExpressionString(this: Address6, substringSearch: boolean = false): string {
+ let output: string[] = [];
+
+ // TODO: revisit why this is necessary
+ const address6 = new Address6(this.correctForm());
+
+ if (address6.elidedGroups === 0) {
+ // The simple case
+ output.push(simpleRegularExpression(address6.parsedAddress));
+ } else if (address6.elidedGroups === constants6.GROUPS) {
+ // A completely elided address
+ output.push(possibleElisions(constants6.GROUPS));
+ } else {
+ // A partially elided address
+ const halves = address6.address.split('::');
+
+ if (halves[0].length) {
+ output.push(simpleRegularExpression(halves[0].split(':')));
+ }
+
+ assert(typeof address6.elidedGroups === 'number');
+
+ output.push(
+ possibleElisions(address6.elidedGroups, halves[0].length !== 0, halves[1].length !== 0),
+ );
+
+ if (halves[1].length) {
+ output.push(simpleRegularExpression(halves[1].split(':')));
+ }
+
+ output = [output.join(':')];
+ }
+
+ if (!substringSearch) {
+ output = [
+ '(?=^|',
+ ADDRESS_BOUNDARY,
+ '|[^\\w\\:])(',
+ ...output,
+ ')(?=[^\\w\\:]|',
+ ADDRESS_BOUNDARY,
+ '|$)',
+ ];
+ }
+
+ return output.join('');
+ }
+
+ /**
+ * Generate a regular expression that can be used to find or validate all
+ * variations of this address.
+ * @memberof Address6
+ * @instance
+ * @param {boolean} substringSearch
+ * @returns {RegExp}
+ */
+ regularExpression(this: Address6, substringSearch: boolean = false): RegExp {
+ return new RegExp(this.regularExpressionString(substringSearch), 'i');
+ }
+ // #endregion
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v4/constants.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v4/constants.ts
new file mode 100644
index 000000000..73c32f1fd
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v4/constants.ts
@@ -0,0 +1,7 @@
+export const BITS = 32;
+export const GROUPS = 4;
+
+export const RE_ADDRESS =
+ /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;
+
+export const RE_SUBNET_STRING = /\/\d{1,2}$/;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/constants.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/constants.ts
new file mode 100644
index 000000000..e23bfa56f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/constants.ts
@@ -0,0 +1,79 @@
+export const BITS = 128;
+export const GROUPS = 8;
+
+/**
+ * Represents IPv6 address scopes
+ * @memberof Address6
+ * @static
+ */
+export const SCOPES: { [key: number]: string | undefined } = {
+ 0: 'Reserved',
+ 1: 'Interface local',
+ 2: 'Link local',
+ 4: 'Admin local',
+ 5: 'Site local',
+ 8: 'Organization local',
+ 14: 'Global',
+ 15: 'Reserved',
+} as const;
+
+/**
+ * Represents IPv6 address types
+ * @memberof Address6
+ * @static
+ */
+export const TYPES: { [key: string]: string | undefined } = {
+ 'ff01::1/128': 'Multicast (All nodes on this interface)',
+ 'ff01::2/128': 'Multicast (All routers on this interface)',
+ 'ff02::1/128': 'Multicast (All nodes on this link)',
+ 'ff02::2/128': 'Multicast (All routers on this link)',
+ 'ff05::2/128': 'Multicast (All routers in this site)',
+ 'ff02::5/128': 'Multicast (OSPFv3 AllSPF routers)',
+ 'ff02::6/128': 'Multicast (OSPFv3 AllDR routers)',
+ 'ff02::9/128': 'Multicast (RIP routers)',
+ 'ff02::a/128': 'Multicast (EIGRP routers)',
+ 'ff02::d/128': 'Multicast (PIM routers)',
+ 'ff02::16/128': 'Multicast (MLDv2 reports)',
+ 'ff01::fb/128': 'Multicast (mDNSv6)',
+ 'ff02::fb/128': 'Multicast (mDNSv6)',
+ 'ff05::fb/128': 'Multicast (mDNSv6)',
+ 'ff02::1:2/128': 'Multicast (All DHCP servers and relay agents on this link)',
+ 'ff05::1:2/128': 'Multicast (All DHCP servers and relay agents in this site)',
+ 'ff02::1:3/128': 'Multicast (All DHCP servers on this link)',
+ 'ff05::1:3/128': 'Multicast (All DHCP servers in this site)',
+ '::/128': 'Unspecified',
+ '::1/128': 'Loopback',
+ 'ff00::/8': 'Multicast',
+ 'fe80::/10': 'Link-local unicast',
+} as const;
+
+/**
+ * A regular expression that matches bad characters in an IPv6 address
+ * @memberof Address6
+ * @static
+ */
+export const RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi;
+
+/**
+ * A regular expression that matches an incorrect IPv6 address
+ * @memberof Address6
+ * @static
+ */
+export const RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\/$)/gi;
+
+/**
+ * A regular expression that matches an IPv6 subnet
+ * @memberof Address6
+ * @static
+ */
+export const RE_SUBNET_STRING = /\/\d{1,3}(?=%|$)/;
+
+/**
+ * A regular expression that matches an IPv6 zone
+ * @memberof Address6
+ * @static
+ */
+export const RE_ZONE_STRING = /%.*$/;
+
+export const RE_URL = /^\[{0,1}([0-9a-f:]+)\]{0,1}/;
+export const RE_URL_WITH_PORT = /\[([0-9a-f:]+)\]:([0-9]{1,5})/;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/helpers.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/helpers.ts
new file mode 100644
index 000000000..e40074c9f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/helpers.ts
@@ -0,0 +1,48 @@
+/**
+ * @returns {String} the string with all zeroes contained in a
+ */
+export function spanAllZeroes(s: string): string {
+ return s.replace(/(0+)/g, '$1 ');
+}
+
+/**
+ * @returns {String} the string with each character contained in a
+ */
+export function spanAll(s: string, offset: number = 0): string {
+ const letters = s.split('');
+
+ return letters
+ .map(
+ (n, i) => `${spanAllZeroes(n)} `,
+ )
+ .join('');
+}
+
+function spanLeadingZeroesSimple(group: string): string {
+ return group.replace(/^(0+)/, '$1 ');
+}
+
+/**
+ * @returns {String} the string with leading zeroes contained in a
+ */
+export function spanLeadingZeroes(address: string): string {
+ const groups = address.split(':');
+
+ return groups.map((g) => spanLeadingZeroesSimple(g)).join(':');
+}
+
+/**
+ * Groups an address
+ * @returns {String} a grouped address
+ */
+export function simpleGroup(addressString: string, offset: number = 0): string[] {
+ const groups = addressString.split(':');
+
+ return groups.map((g, i) => {
+ if (/group-v4/.test(g)) {
+ return g;
+ }
+
+ return `${spanLeadingZeroesSimple(g)} `;
+ });
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/regular-expressions.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/regular-expressions.ts
new file mode 100644
index 000000000..a511b8499
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ip-address/src/v6/regular-expressions.ts
@@ -0,0 +1,94 @@
+import * as v6 from './constants';
+
+export function groupPossibilities(possibilities: string[]): string {
+ return `(${possibilities.join('|')})`;
+}
+
+export function padGroup(group: string): string {
+ if (group.length < 4) {
+ return `0{0,${4 - group.length}}${group}`;
+ }
+
+ return group;
+}
+
+export const ADDRESS_BOUNDARY = '[^A-Fa-f0-9:]';
+
+export function simpleRegularExpression(groups: string[]) {
+ const zeroIndexes: number[] = [];
+
+ groups.forEach((group, i) => {
+ const groupInteger = parseInt(group, 16);
+
+ if (groupInteger === 0) {
+ zeroIndexes.push(i);
+ }
+ });
+
+ // You can technically elide a single 0, this creates the regular expressions
+ // to match that eventuality
+ const possibilities = zeroIndexes.map((zeroIndex) =>
+ groups
+ .map((group, i) => {
+ if (i === zeroIndex) {
+ const elision = i === 0 || i === v6.GROUPS - 1 ? ':' : '';
+
+ return groupPossibilities([padGroup(group), elision]);
+ }
+
+ return padGroup(group);
+ })
+ .join(':'),
+ );
+
+ // The simplest case
+ possibilities.push(groups.map(padGroup).join(':'));
+
+ return groupPossibilities(possibilities);
+}
+
+export function possibleElisions(
+ elidedGroups: number,
+ moreLeft?: boolean,
+ moreRight?: boolean,
+): string {
+ const left = moreLeft ? '' : ':';
+ const right = moreRight ? '' : ':';
+
+ const possibilities = [];
+
+ // 1. elision of everything (::)
+ if (!moreLeft && !moreRight) {
+ possibilities.push('::');
+ }
+
+ // 2. complete elision of the middle
+ if (moreLeft && moreRight) {
+ possibilities.push('');
+ }
+
+ if ((moreRight && !moreLeft) || (!moreRight && moreLeft)) {
+ // 3. complete elision of one side
+ possibilities.push(':');
+ }
+
+ // 4. elision from the left side
+ possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`);
+
+ // 5. elision from the right side
+ possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`);
+
+ // 6. no elision
+ possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`);
+
+ // 7. elision (including sloppy elision) from the middle
+ for (let groups = 1; groups < elidedGroups - 1; groups++) {
+ for (let position = 1; position < elidedGroups - groups; position++) {
+ possibilities.push(
+ `(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`,
+ );
+ }
+ }
+
+ return groupPossibilities(possibilities);
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/LICENSE
new file mode 100644
index 000000000..f6b37b52d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2011-2017 whitequark
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/README.md
new file mode 100644
index 000000000..f57725b0f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/README.md
@@ -0,0 +1,233 @@
+# ipaddr.js — an IPv6 and IPv4 address manipulation library [](https://travis-ci.org/whitequark/ipaddr.js)
+
+ipaddr.js is a small (1.9K minified and gzipped) library for manipulating
+IP addresses in JavaScript environments. It runs on both CommonJS runtimes
+(e.g. [nodejs]) and in a web browser.
+
+ipaddr.js allows you to verify and parse string representation of an IP
+address, match it against a CIDR range or range list, determine if it falls
+into some reserved ranges (examples include loopback and private ranges),
+and convert between IPv4 and IPv4-mapped IPv6 addresses.
+
+[nodejs]: http://nodejs.org
+
+## Installation
+
+`npm install ipaddr.js`
+
+or
+
+`bower install ipaddr.js`
+
+## API
+
+ipaddr.js defines one object in the global scope: `ipaddr`. In CommonJS,
+it is exported from the module:
+
+```js
+var ipaddr = require('ipaddr.js');
+```
+
+The API consists of several global methods and two classes: ipaddr.IPv6 and ipaddr.IPv4.
+
+### Global methods
+
+There are three global methods defined: `ipaddr.isValid`, `ipaddr.parse` and
+`ipaddr.process`. All of them receive a string as a single parameter.
+
+The `ipaddr.isValid` method returns `true` if the address is a valid IPv4 or
+IPv6 address, and `false` otherwise. It does not throw any exceptions.
+
+The `ipaddr.parse` method returns an object representing the IP address,
+or throws an `Error` if the passed string is not a valid representation of an
+IP address.
+
+The `ipaddr.process` method works just like the `ipaddr.parse` one, but it
+automatically converts IPv4-mapped IPv6 addresses to their IPv4 counterparts
+before returning. It is useful when you have a Node.js instance listening
+on an IPv6 socket, and the `net.ivp6.bindv6only` sysctl parameter (or its
+equivalent on non-Linux OS) is set to 0. In this case, you can accept IPv4
+connections on your IPv6-only socket, but the remote address will be mangled.
+Use `ipaddr.process` method to automatically demangle it.
+
+### Object representation
+
+Parsing methods return an object which descends from `ipaddr.IPv6` or
+`ipaddr.IPv4`. These objects share some properties, but most of them differ.
+
+#### Shared properties
+
+One can determine the type of address by calling `addr.kind()`. It will return
+either `"ipv6"` or `"ipv4"`.
+
+An address can be converted back to its string representation with `addr.toString()`.
+Note that this method:
+ * does not return the original string used to create the object (in fact, there is
+ no way of getting that string)
+ * returns a compact representation (when it is applicable)
+
+A `match(range, bits)` method can be used to check if the address falls into a
+certain CIDR range.
+Note that an address can be (obviously) matched only against an address of the same type.
+
+For example:
+
+```js
+var addr = ipaddr.parse("2001:db8:1234::1");
+var range = ipaddr.parse("2001:db8::");
+
+addr.match(range, 32); // => true
+```
+
+Alternatively, `match` can also be called as `match([range, bits])`. In this way,
+it can be used together with the `parseCIDR(string)` method, which parses an IP
+address together with a CIDR range.
+
+For example:
+
+```js
+var addr = ipaddr.parse("2001:db8:1234::1");
+
+addr.match(ipaddr.parseCIDR("2001:db8::/32")); // => true
+```
+
+A `range()` method returns one of predefined names for several special ranges defined
+by IP protocols. The exact names (and their respective CIDR ranges) can be looked up
+in the source: [IPv6 ranges] and [IPv4 ranges]. Some common ones include `"unicast"`
+(the default one) and `"reserved"`.
+
+You can match against your own range list by using
+`ipaddr.subnetMatch(address, rangeList, defaultName)` method. It can work with a mix of IPv6 or IPv4 addresses, and accepts a name-to-subnet map as the range list. For example:
+
+```js
+var rangeList = {
+ documentationOnly: [ ipaddr.parse('2001:db8::'), 32 ],
+ tunnelProviders: [
+ [ ipaddr.parse('2001:470::'), 32 ], // he.net
+ [ ipaddr.parse('2001:5c0::'), 32 ] // freenet6
+ ]
+};
+ipaddr.subnetMatch(ipaddr.parse('2001:470:8:66::1'), rangeList, 'unknown'); // => "tunnelProviders"
+```
+
+The addresses can be converted to their byte representation with `toByteArray()`.
+(Actually, JavaScript mostly does not know about byte buffers. They are emulated with
+arrays of numbers, each in range of 0..255.)
+
+```js
+var bytes = ipaddr.parse('2a00:1450:8007::68').toByteArray(); // ipv6.google.com
+bytes // => [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, , 0x00, 0x68 ]
+```
+
+The `ipaddr.IPv4` and `ipaddr.IPv6` objects have some methods defined, too. All of them
+have the same interface for both protocols, and are similar to global methods.
+
+`ipaddr.IPvX.isValid(string)` can be used to check if the string is a valid address
+for particular protocol, and `ipaddr.IPvX.parse(string)` is the error-throwing parser.
+
+`ipaddr.IPvX.isValid(string)` uses the same format for parsing as the POSIX `inet_ntoa` function, which accepts unusual formats like `0xc0.168.1.1` or `0x10000000`. The function `ipaddr.IPv4.isValidFourPartDecimal(string)` validates the IPv4 address and also ensures that it is written in four-part decimal format.
+
+[IPv6 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/src/ipaddr.coffee#L186
+[IPv4 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/src/ipaddr.coffee#L71
+
+#### IPv6 properties
+
+Sometimes you will want to convert IPv6 not to a compact string representation (with
+the `::` substitution); the `toNormalizedString()` method will return an address where
+all zeroes are explicit.
+
+For example:
+
+```js
+var addr = ipaddr.parse("2001:0db8::0001");
+addr.toString(); // => "2001:db8::1"
+addr.toNormalizedString(); // => "2001:db8:0:0:0:0:0:1"
+```
+
+The `isIPv4MappedAddress()` method will return `true` if this address is an IPv4-mapped
+one, and `toIPv4Address()` will return an IPv4 object address.
+
+To access the underlying binary representation of the address, use `addr.parts`.
+
+```js
+var addr = ipaddr.parse("2001:db8:10::1234:DEAD");
+addr.parts // => [0x2001, 0xdb8, 0x10, 0, 0, 0, 0x1234, 0xdead]
+```
+
+A IPv6 zone index can be accessed via `addr.zoneId`:
+
+```js
+var addr = ipaddr.parse("2001:db8::%eth0");
+addr.zoneId // => 'eth0'
+```
+
+#### IPv4 properties
+
+`toIPv4MappedAddress()` will return a corresponding IPv4-mapped IPv6 address.
+
+To access the underlying representation of the address, use `addr.octets`.
+
+```js
+var addr = ipaddr.parse("192.168.1.1");
+addr.octets // => [192, 168, 1, 1]
+```
+
+`prefixLengthFromSubnetMask()` will return a CIDR prefix length for a valid IPv4 netmask or
+null if the netmask is not valid.
+
+```js
+ipaddr.IPv4.parse('255.255.255.240').prefixLengthFromSubnetMask() == 28
+ipaddr.IPv4.parse('255.192.164.0').prefixLengthFromSubnetMask() == null
+```
+
+`subnetMaskFromPrefixLength()` will return an IPv4 netmask for a valid CIDR prefix length.
+
+```js
+ipaddr.IPv4.subnetMaskFromPrefixLength(24) == "255.255.255.0"
+ipaddr.IPv4.subnetMaskFromPrefixLength(29) == "255.255.255.248"
+```
+
+`broadcastAddressFromCIDR()` will return the broadcast address for a given IPv4 interface and netmask in CIDR notation.
+```js
+ipaddr.IPv4.broadcastAddressFromCIDR("172.0.0.1/24") == "172.0.0.255"
+```
+`networkAddressFromCIDR()` will return the network address for a given IPv4 interface and netmask in CIDR notation.
+```js
+ipaddr.IPv4.networkAddressFromCIDR("172.0.0.1/24") == "172.0.0.0"
+```
+
+#### Conversion
+
+IPv4 and IPv6 can be converted bidirectionally to and from network byte order (MSB) byte arrays.
+
+The `fromByteArray()` method will take an array and create an appropriate IPv4 or IPv6 object
+if the input satisfies the requirements. For IPv4 it has to be an array of four 8-bit values,
+while for IPv6 it has to be an array of sixteen 8-bit values.
+
+For example:
+```js
+var addr = ipaddr.fromByteArray([0x7f, 0, 0, 1]);
+addr.toString(); // => "127.0.0.1"
+```
+
+or
+
+```js
+var addr = ipaddr.fromByteArray([0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
+addr.toString(); // => "2001:db8::1"
+```
+
+Both objects also offer a `toByteArray()` method, which returns an array in network byte order (MSB).
+
+For example:
+```js
+var addr = ipaddr.parse("127.0.0.1");
+addr.toByteArray(); // => [0x7f, 0, 0, 1]
+```
+
+or
+
+```js
+var addr = ipaddr.parse("2001:db8::1");
+addr.toByteArray(); // => [0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
+```
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/ipaddr.min.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/ipaddr.min.js
new file mode 100644
index 000000000..b54a7cc42
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/ipaddr.min.js
@@ -0,0 +1 @@
+(function(){var r,t,n,e,i,o,a,s;t={},s=this,"undefined"!=typeof module&&null!==module&&module.exports?module.exports=t:s.ipaddr=t,a=function(r,t,n,e){var i,o;if(r.length!==t.length)throw new Error("ipaddr: cannot match CIDR for objects with different lengths");for(i=0;e>0;){if((o=n-e)<0&&(o=0),r[i]>>o!=t[i]>>o)return!1;e-=n,i+=1}return!0},t.subnetMatch=function(r,t,n){var e,i,o,a,s;null==n&&(n="unicast");for(o in t)for(!(a=t[o])[0]||a[0]instanceof Array||(a=[a]),e=0,i=a.length;e=0;t=n+=-1){if(!((e=this.octets[t])in a))return null;if(o=a[e],i&&0!==o)return null;8!==o&&(i=!0),r+=o}return 32-r},r}(),n="(0?\\d+|0x[a-f0-9]+)",e={fourOctet:new RegExp("^"+n+"\\."+n+"\\."+n+"\\."+n+"$","i"),longValue:new RegExp("^"+n+"$","i")},t.IPv4.parser=function(r){var t,n,i,o,a;if(n=function(r){return"0"===r[0]&&"x"!==r[1]?parseInt(r,8):parseInt(r)},t=r.match(e.fourOctet))return function(){var r,e,o,a;for(a=[],r=0,e=(o=t.slice(1,6)).length;r4294967295||a<0)throw new Error("ipaddr: address outside defined range");return function(){var r,t;for(t=[],o=r=0;r<=24;o=r+=8)t.push(a>>o&255);return t}().reverse()}return null},t.IPv6=function(){function r(r,t){var n,e,i,o,a,s;if(16===r.length)for(this.parts=[],n=e=0;e<=14;n=e+=2)this.parts.push(r[n]<<8|r[n+1]);else{if(8!==r.length)throw new Error("ipaddr: ipv6 part count should be 8 or 16");this.parts=r}for(i=0,o=(s=this.parts).length;it&&(r=n.index,t=n[0].length);return t<0?i:i.substring(0,r)+"::"+i.substring(r+t)},r.prototype.toByteArray=function(){var r,t,n,e,i;for(r=[],t=0,n=(i=this.parts).length;t>8),r.push(255&e);return r},r.prototype.toNormalizedString=function(){var r,t,n;return r=function(){var r,n,e,i;for(i=[],r=0,n=(e=this.parts).length;r>8,255&r,n>>8,255&n])},r.prototype.prefixLengthFromSubnetMask=function(){var r,t,n,e,i,o,a;for(a={0:16,32768:15,49152:14,57344:13,61440:12,63488:11,64512:10,65024:9,65280:8,65408:7,65472:6,65504:5,65520:4,65528:3,65532:2,65534:1,65535:0},r=0,i=!1,t=n=7;n>=0;t=n+=-1){if(!((e=this.parts[t])in a))return null;if(o=a[e],i&&0!==o)return null;16!==o&&(i=!0),r+=o}return 128-r},r}(),i="(?:[0-9a-f]+::?)+",o={zoneIndex:new RegExp("%[0-9a-z]{1,}","i"),native:new RegExp("^(::)?("+i+")?([0-9a-f]+)?(::)?(%[0-9a-z]{1,})?$","i"),transitional:new RegExp("^((?:"+i+")|(?:::)(?:"+i+")?)"+n+"\\."+n+"\\."+n+"\\."+n+"(%[0-9a-z]{1,})?$","i")},r=function(r,t){var n,e,i,a,s,p;if(r.indexOf("::")!==r.lastIndexOf("::"))return null;for((p=(r.match(o.zoneIndex)||[])[0])&&(p=p.substring(1),r=r.replace(/%.+$/,"")),n=0,e=-1;(e=r.indexOf(":",e+1))>=0;)n++;if("::"===r.substr(0,2)&&n--,"::"===r.substr(-2,2)&&n--,n>t)return null;for(s=t-n,a=":";s--;)a+="0:";return":"===(r=r.replace("::",a))[0]&&(r=r.slice(1)),":"===r[r.length-1]&&(r=r.slice(0,-1)),t=function(){var t,n,e,o;for(o=[],t=0,n=(e=r.split(":")).length;t=0&&t<=32)return e=[this.parse(n[1]),t],Object.defineProperty(e,"toString",{value:function(){return this.join("/")}}),e;throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range")},t.IPv4.subnetMaskFromPrefixLength=function(r){var t,n,e;if((r=parseInt(r))<0||r>32)throw new Error("ipaddr: invalid IPv4 prefix length");for(e=[0,0,0,0],n=0,t=Math.floor(r/8);n=0&&t<=128)return e=[this.parse(n[1]),t],Object.defineProperty(e,"toString",{value:function(){return this.join("/")}}),e;throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range")},t.isValid=function(r){return t.IPv6.isValid(r)||t.IPv4.isValid(r)},t.parse=function(r){if(t.IPv6.isValid(r))return t.IPv6.parse(r);if(t.IPv4.isValid(r))return t.IPv4.parse(r);throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format")},t.parseCIDR=function(r){try{return t.IPv6.parseCIDR(r)}catch(n){n;try{return t.IPv4.parseCIDR(r)}catch(r){throw r,new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format")}}},t.fromByteArray=function(r){var n;if(4===(n=r.length))return new t.IPv4(r);if(16===n)return new t.IPv6(r);throw new Error("ipaddr: the binary input is neither an IPv6 nor IPv4 address")},t.process=function(r){var t;return t=this.parse(r),"ipv6"===t.kind()&&t.isIPv4MappedAddress()?t.toIPv4Address():t}}).call(this);
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/lib/ipaddr.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/lib/ipaddr.js
new file mode 100644
index 000000000..18bd93b5e
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/lib/ipaddr.js
@@ -0,0 +1,673 @@
+(function() {
+ var expandIPv6, ipaddr, ipv4Part, ipv4Regexes, ipv6Part, ipv6Regexes, matchCIDR, root, zoneIndex;
+
+ ipaddr = {};
+
+ root = this;
+
+ if ((typeof module !== "undefined" && module !== null) && module.exports) {
+ module.exports = ipaddr;
+ } else {
+ root['ipaddr'] = ipaddr;
+ }
+
+ matchCIDR = function(first, second, partSize, cidrBits) {
+ var part, shift;
+ if (first.length !== second.length) {
+ throw new Error("ipaddr: cannot match CIDR for objects with different lengths");
+ }
+ part = 0;
+ while (cidrBits > 0) {
+ shift = partSize - cidrBits;
+ if (shift < 0) {
+ shift = 0;
+ }
+ if (first[part] >> shift !== second[part] >> shift) {
+ return false;
+ }
+ cidrBits -= partSize;
+ part += 1;
+ }
+ return true;
+ };
+
+ ipaddr.subnetMatch = function(address, rangeList, defaultName) {
+ var k, len, rangeName, rangeSubnets, subnet;
+ if (defaultName == null) {
+ defaultName = 'unicast';
+ }
+ for (rangeName in rangeList) {
+ rangeSubnets = rangeList[rangeName];
+ if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) {
+ rangeSubnets = [rangeSubnets];
+ }
+ for (k = 0, len = rangeSubnets.length; k < len; k++) {
+ subnet = rangeSubnets[k];
+ if (address.kind() === subnet[0].kind()) {
+ if (address.match.apply(address, subnet)) {
+ return rangeName;
+ }
+ }
+ }
+ }
+ return defaultName;
+ };
+
+ ipaddr.IPv4 = (function() {
+ function IPv4(octets) {
+ var k, len, octet;
+ if (octets.length !== 4) {
+ throw new Error("ipaddr: ipv4 octet count should be 4");
+ }
+ for (k = 0, len = octets.length; k < len; k++) {
+ octet = octets[k];
+ if (!((0 <= octet && octet <= 255))) {
+ throw new Error("ipaddr: ipv4 octet should fit in 8 bits");
+ }
+ }
+ this.octets = octets;
+ }
+
+ IPv4.prototype.kind = function() {
+ return 'ipv4';
+ };
+
+ IPv4.prototype.toString = function() {
+ return this.octets.join(".");
+ };
+
+ IPv4.prototype.toNormalizedString = function() {
+ return this.toString();
+ };
+
+ IPv4.prototype.toByteArray = function() {
+ return this.octets.slice(0);
+ };
+
+ IPv4.prototype.match = function(other, cidrRange) {
+ var ref;
+ if (cidrRange === void 0) {
+ ref = other, other = ref[0], cidrRange = ref[1];
+ }
+ if (other.kind() !== 'ipv4') {
+ throw new Error("ipaddr: cannot match ipv4 address with non-ipv4 one");
+ }
+ return matchCIDR(this.octets, other.octets, 8, cidrRange);
+ };
+
+ IPv4.prototype.SpecialRanges = {
+ unspecified: [[new IPv4([0, 0, 0, 0]), 8]],
+ broadcast: [[new IPv4([255, 255, 255, 255]), 32]],
+ multicast: [[new IPv4([224, 0, 0, 0]), 4]],
+ linkLocal: [[new IPv4([169, 254, 0, 0]), 16]],
+ loopback: [[new IPv4([127, 0, 0, 0]), 8]],
+ carrierGradeNat: [[new IPv4([100, 64, 0, 0]), 10]],
+ "private": [[new IPv4([10, 0, 0, 0]), 8], [new IPv4([172, 16, 0, 0]), 12], [new IPv4([192, 168, 0, 0]), 16]],
+ reserved: [[new IPv4([192, 0, 0, 0]), 24], [new IPv4([192, 0, 2, 0]), 24], [new IPv4([192, 88, 99, 0]), 24], [new IPv4([198, 51, 100, 0]), 24], [new IPv4([203, 0, 113, 0]), 24], [new IPv4([240, 0, 0, 0]), 4]]
+ };
+
+ IPv4.prototype.range = function() {
+ return ipaddr.subnetMatch(this, this.SpecialRanges);
+ };
+
+ IPv4.prototype.toIPv4MappedAddress = function() {
+ return ipaddr.IPv6.parse("::ffff:" + (this.toString()));
+ };
+
+ IPv4.prototype.prefixLengthFromSubnetMask = function() {
+ var cidr, i, k, octet, stop, zeros, zerotable;
+ zerotable = {
+ 0: 8,
+ 128: 7,
+ 192: 6,
+ 224: 5,
+ 240: 4,
+ 248: 3,
+ 252: 2,
+ 254: 1,
+ 255: 0
+ };
+ cidr = 0;
+ stop = false;
+ for (i = k = 3; k >= 0; i = k += -1) {
+ octet = this.octets[i];
+ if (octet in zerotable) {
+ zeros = zerotable[octet];
+ if (stop && zeros !== 0) {
+ return null;
+ }
+ if (zeros !== 8) {
+ stop = true;
+ }
+ cidr += zeros;
+ } else {
+ return null;
+ }
+ }
+ return 32 - cidr;
+ };
+
+ return IPv4;
+
+ })();
+
+ ipv4Part = "(0?\\d+|0x[a-f0-9]+)";
+
+ ipv4Regexes = {
+ fourOctet: new RegExp("^" + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "$", 'i'),
+ longValue: new RegExp("^" + ipv4Part + "$", 'i')
+ };
+
+ ipaddr.IPv4.parser = function(string) {
+ var match, parseIntAuto, part, shift, value;
+ parseIntAuto = function(string) {
+ if (string[0] === "0" && string[1] !== "x") {
+ return parseInt(string, 8);
+ } else {
+ return parseInt(string);
+ }
+ };
+ if (match = string.match(ipv4Regexes.fourOctet)) {
+ return (function() {
+ var k, len, ref, results;
+ ref = match.slice(1, 6);
+ results = [];
+ for (k = 0, len = ref.length; k < len; k++) {
+ part = ref[k];
+ results.push(parseIntAuto(part));
+ }
+ return results;
+ })();
+ } else if (match = string.match(ipv4Regexes.longValue)) {
+ value = parseIntAuto(match[1]);
+ if (value > 0xffffffff || value < 0) {
+ throw new Error("ipaddr: address outside defined range");
+ }
+ return ((function() {
+ var k, results;
+ results = [];
+ for (shift = k = 0; k <= 24; shift = k += 8) {
+ results.push((value >> shift) & 0xff);
+ }
+ return results;
+ })()).reverse();
+ } else {
+ return null;
+ }
+ };
+
+ ipaddr.IPv6 = (function() {
+ function IPv6(parts, zoneId) {
+ var i, k, l, len, part, ref;
+ if (parts.length === 16) {
+ this.parts = [];
+ for (i = k = 0; k <= 14; i = k += 2) {
+ this.parts.push((parts[i] << 8) | parts[i + 1]);
+ }
+ } else if (parts.length === 8) {
+ this.parts = parts;
+ } else {
+ throw new Error("ipaddr: ipv6 part count should be 8 or 16");
+ }
+ ref = this.parts;
+ for (l = 0, len = ref.length; l < len; l++) {
+ part = ref[l];
+ if (!((0 <= part && part <= 0xffff))) {
+ throw new Error("ipaddr: ipv6 part should fit in 16 bits");
+ }
+ }
+ if (zoneId) {
+ this.zoneId = zoneId;
+ }
+ }
+
+ IPv6.prototype.kind = function() {
+ return 'ipv6';
+ };
+
+ IPv6.prototype.toString = function() {
+ return this.toNormalizedString().replace(/((^|:)(0(:|$))+)/, '::');
+ };
+
+ IPv6.prototype.toRFC5952String = function() {
+ var bestMatchIndex, bestMatchLength, match, regex, string;
+ regex = /((^|:)(0(:|$)){2,})/g;
+ string = this.toNormalizedString();
+ bestMatchIndex = 0;
+ bestMatchLength = -1;
+ while ((match = regex.exec(string))) {
+ if (match[0].length > bestMatchLength) {
+ bestMatchIndex = match.index;
+ bestMatchLength = match[0].length;
+ }
+ }
+ if (bestMatchLength < 0) {
+ return string;
+ }
+ return string.substring(0, bestMatchIndex) + '::' + string.substring(bestMatchIndex + bestMatchLength);
+ };
+
+ IPv6.prototype.toByteArray = function() {
+ var bytes, k, len, part, ref;
+ bytes = [];
+ ref = this.parts;
+ for (k = 0, len = ref.length; k < len; k++) {
+ part = ref[k];
+ bytes.push(part >> 8);
+ bytes.push(part & 0xff);
+ }
+ return bytes;
+ };
+
+ IPv6.prototype.toNormalizedString = function() {
+ var addr, part, suffix;
+ addr = ((function() {
+ var k, len, ref, results;
+ ref = this.parts;
+ results = [];
+ for (k = 0, len = ref.length; k < len; k++) {
+ part = ref[k];
+ results.push(part.toString(16));
+ }
+ return results;
+ }).call(this)).join(":");
+ suffix = '';
+ if (this.zoneId) {
+ suffix = '%' + this.zoneId;
+ }
+ return addr + suffix;
+ };
+
+ IPv6.prototype.toFixedLengthString = function() {
+ var addr, part, suffix;
+ addr = ((function() {
+ var k, len, ref, results;
+ ref = this.parts;
+ results = [];
+ for (k = 0, len = ref.length; k < len; k++) {
+ part = ref[k];
+ results.push(part.toString(16).padStart(4, '0'));
+ }
+ return results;
+ }).call(this)).join(":");
+ suffix = '';
+ if (this.zoneId) {
+ suffix = '%' + this.zoneId;
+ }
+ return addr + suffix;
+ };
+
+ IPv6.prototype.match = function(other, cidrRange) {
+ var ref;
+ if (cidrRange === void 0) {
+ ref = other, other = ref[0], cidrRange = ref[1];
+ }
+ if (other.kind() !== 'ipv6') {
+ throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one");
+ }
+ return matchCIDR(this.parts, other.parts, 16, cidrRange);
+ };
+
+ IPv6.prototype.SpecialRanges = {
+ unspecified: [new IPv6([0, 0, 0, 0, 0, 0, 0, 0]), 128],
+ linkLocal: [new IPv6([0xfe80, 0, 0, 0, 0, 0, 0, 0]), 10],
+ multicast: [new IPv6([0xff00, 0, 0, 0, 0, 0, 0, 0]), 8],
+ loopback: [new IPv6([0, 0, 0, 0, 0, 0, 0, 1]), 128],
+ uniqueLocal: [new IPv6([0xfc00, 0, 0, 0, 0, 0, 0, 0]), 7],
+ ipv4Mapped: [new IPv6([0, 0, 0, 0, 0, 0xffff, 0, 0]), 96],
+ rfc6145: [new IPv6([0, 0, 0, 0, 0xffff, 0, 0, 0]), 96],
+ rfc6052: [new IPv6([0x64, 0xff9b, 0, 0, 0, 0, 0, 0]), 96],
+ '6to4': [new IPv6([0x2002, 0, 0, 0, 0, 0, 0, 0]), 16],
+ teredo: [new IPv6([0x2001, 0, 0, 0, 0, 0, 0, 0]), 32],
+ reserved: [[new IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32]]
+ };
+
+ IPv6.prototype.range = function() {
+ return ipaddr.subnetMatch(this, this.SpecialRanges);
+ };
+
+ IPv6.prototype.isIPv4MappedAddress = function() {
+ return this.range() === 'ipv4Mapped';
+ };
+
+ IPv6.prototype.toIPv4Address = function() {
+ var high, low, ref;
+ if (!this.isIPv4MappedAddress()) {
+ throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4");
+ }
+ ref = this.parts.slice(-2), high = ref[0], low = ref[1];
+ return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff]);
+ };
+
+ IPv6.prototype.prefixLengthFromSubnetMask = function() {
+ var cidr, i, k, part, stop, zeros, zerotable;
+ zerotable = {
+ 0: 16,
+ 32768: 15,
+ 49152: 14,
+ 57344: 13,
+ 61440: 12,
+ 63488: 11,
+ 64512: 10,
+ 65024: 9,
+ 65280: 8,
+ 65408: 7,
+ 65472: 6,
+ 65504: 5,
+ 65520: 4,
+ 65528: 3,
+ 65532: 2,
+ 65534: 1,
+ 65535: 0
+ };
+ cidr = 0;
+ stop = false;
+ for (i = k = 7; k >= 0; i = k += -1) {
+ part = this.parts[i];
+ if (part in zerotable) {
+ zeros = zerotable[part];
+ if (stop && zeros !== 0) {
+ return null;
+ }
+ if (zeros !== 16) {
+ stop = true;
+ }
+ cidr += zeros;
+ } else {
+ return null;
+ }
+ }
+ return 128 - cidr;
+ };
+
+ return IPv6;
+
+ })();
+
+ ipv6Part = "(?:[0-9a-f]+::?)+";
+
+ zoneIndex = "%[0-9a-z]{1,}";
+
+ ipv6Regexes = {
+ zoneIndex: new RegExp(zoneIndex, 'i'),
+ "native": new RegExp("^(::)?(" + ipv6Part + ")?([0-9a-f]+)?(::)?(" + zoneIndex + ")?$", 'i'),
+ transitional: new RegExp(("^((?:" + ipv6Part + ")|(?:::)(?:" + ipv6Part + ")?)") + (ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part + "\\." + ipv4Part) + ("(" + zoneIndex + ")?$"), 'i')
+ };
+
+ expandIPv6 = function(string, parts) {
+ var colonCount, lastColon, part, replacement, replacementCount, zoneId;
+ if (string.indexOf('::') !== string.lastIndexOf('::')) {
+ return null;
+ }
+ zoneId = (string.match(ipv6Regexes['zoneIndex']) || [])[0];
+ if (zoneId) {
+ zoneId = zoneId.substring(1);
+ string = string.replace(/%.+$/, '');
+ }
+ colonCount = 0;
+ lastColon = -1;
+ while ((lastColon = string.indexOf(':', lastColon + 1)) >= 0) {
+ colonCount++;
+ }
+ if (string.substr(0, 2) === '::') {
+ colonCount--;
+ }
+ if (string.substr(-2, 2) === '::') {
+ colonCount--;
+ }
+ if (colonCount > parts) {
+ return null;
+ }
+ replacementCount = parts - colonCount;
+ replacement = ':';
+ while (replacementCount--) {
+ replacement += '0:';
+ }
+ string = string.replace('::', replacement);
+ if (string[0] === ':') {
+ string = string.slice(1);
+ }
+ if (string[string.length - 1] === ':') {
+ string = string.slice(0, -1);
+ }
+ parts = (function() {
+ var k, len, ref, results;
+ ref = string.split(":");
+ results = [];
+ for (k = 0, len = ref.length; k < len; k++) {
+ part = ref[k];
+ results.push(parseInt(part, 16));
+ }
+ return results;
+ })();
+ return {
+ parts: parts,
+ zoneId: zoneId
+ };
+ };
+
+ ipaddr.IPv6.parser = function(string) {
+ var addr, k, len, match, octet, octets, zoneId;
+ if (ipv6Regexes['native'].test(string)) {
+ return expandIPv6(string, 8);
+ } else if (match = string.match(ipv6Regexes['transitional'])) {
+ zoneId = match[6] || '';
+ addr = expandIPv6(match[1].slice(0, -1) + zoneId, 6);
+ if (addr.parts) {
+ octets = [parseInt(match[2]), parseInt(match[3]), parseInt(match[4]), parseInt(match[5])];
+ for (k = 0, len = octets.length; k < len; k++) {
+ octet = octets[k];
+ if (!((0 <= octet && octet <= 255))) {
+ return null;
+ }
+ }
+ addr.parts.push(octets[0] << 8 | octets[1]);
+ addr.parts.push(octets[2] << 8 | octets[3]);
+ return {
+ parts: addr.parts,
+ zoneId: addr.zoneId
+ };
+ }
+ }
+ return null;
+ };
+
+ ipaddr.IPv4.isIPv4 = ipaddr.IPv6.isIPv6 = function(string) {
+ return this.parser(string) !== null;
+ };
+
+ ipaddr.IPv4.isValid = function(string) {
+ var e;
+ try {
+ new this(this.parser(string));
+ return true;
+ } catch (error1) {
+ e = error1;
+ return false;
+ }
+ };
+
+ ipaddr.IPv4.isValidFourPartDecimal = function(string) {
+ if (ipaddr.IPv4.isValid(string) && string.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/)) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ ipaddr.IPv6.isValid = function(string) {
+ var addr, e;
+ if (typeof string === "string" && string.indexOf(":") === -1) {
+ return false;
+ }
+ try {
+ addr = this.parser(string);
+ new this(addr.parts, addr.zoneId);
+ return true;
+ } catch (error1) {
+ e = error1;
+ return false;
+ }
+ };
+
+ ipaddr.IPv4.parse = function(string) {
+ var parts;
+ parts = this.parser(string);
+ if (parts === null) {
+ throw new Error("ipaddr: string is not formatted like ip address");
+ }
+ return new this(parts);
+ };
+
+ ipaddr.IPv6.parse = function(string) {
+ var addr;
+ addr = this.parser(string);
+ if (addr.parts === null) {
+ throw new Error("ipaddr: string is not formatted like ip address");
+ }
+ return new this(addr.parts, addr.zoneId);
+ };
+
+ ipaddr.IPv4.parseCIDR = function(string) {
+ var maskLength, match, parsed;
+ if (match = string.match(/^(.+)\/(\d+)$/)) {
+ maskLength = parseInt(match[2]);
+ if (maskLength >= 0 && maskLength <= 32) {
+ parsed = [this.parse(match[1]), maskLength];
+ Object.defineProperty(parsed, 'toString', {
+ value: function() {
+ return this.join('/');
+ }
+ });
+ return parsed;
+ }
+ }
+ throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range");
+ };
+
+ ipaddr.IPv4.subnetMaskFromPrefixLength = function(prefix) {
+ var filledOctetCount, j, octets;
+ prefix = parseInt(prefix);
+ if (prefix < 0 || prefix > 32) {
+ throw new Error('ipaddr: invalid IPv4 prefix length');
+ }
+ octets = [0, 0, 0, 0];
+ j = 0;
+ filledOctetCount = Math.floor(prefix / 8);
+ while (j < filledOctetCount) {
+ octets[j] = 255;
+ j++;
+ }
+ if (filledOctetCount < 4) {
+ octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - (prefix % 8);
+ }
+ return new this(octets);
+ };
+
+ ipaddr.IPv4.broadcastAddressFromCIDR = function(string) {
+ var cidr, error, i, ipInterfaceOctets, octets, subnetMaskOctets;
+ try {
+ cidr = this.parseCIDR(string);
+ ipInterfaceOctets = cidr[0].toByteArray();
+ subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
+ octets = [];
+ i = 0;
+ while (i < 4) {
+ octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255);
+ i++;
+ }
+ return new this(octets);
+ } catch (error1) {
+ error = error1;
+ throw new Error('ipaddr: the address does not have IPv4 CIDR format');
+ }
+ };
+
+ ipaddr.IPv4.networkAddressFromCIDR = function(string) {
+ var cidr, error, i, ipInterfaceOctets, octets, subnetMaskOctets;
+ try {
+ cidr = this.parseCIDR(string);
+ ipInterfaceOctets = cidr[0].toByteArray();
+ subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
+ octets = [];
+ i = 0;
+ while (i < 4) {
+ octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10));
+ i++;
+ }
+ return new this(octets);
+ } catch (error1) {
+ error = error1;
+ throw new Error('ipaddr: the address does not have IPv4 CIDR format');
+ }
+ };
+
+ ipaddr.IPv6.parseCIDR = function(string) {
+ var maskLength, match, parsed;
+ if (match = string.match(/^(.+)\/(\d+)$/)) {
+ maskLength = parseInt(match[2]);
+ if (maskLength >= 0 && maskLength <= 128) {
+ parsed = [this.parse(match[1]), maskLength];
+ Object.defineProperty(parsed, 'toString', {
+ value: function() {
+ return this.join('/');
+ }
+ });
+ return parsed;
+ }
+ }
+ throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range");
+ };
+
+ ipaddr.isValid = function(string) {
+ return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string);
+ };
+
+ ipaddr.parse = function(string) {
+ if (ipaddr.IPv6.isValid(string)) {
+ return ipaddr.IPv6.parse(string);
+ } else if (ipaddr.IPv4.isValid(string)) {
+ return ipaddr.IPv4.parse(string);
+ } else {
+ throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format");
+ }
+ };
+
+ ipaddr.parseCIDR = function(string) {
+ var e;
+ try {
+ return ipaddr.IPv6.parseCIDR(string);
+ } catch (error1) {
+ e = error1;
+ try {
+ return ipaddr.IPv4.parseCIDR(string);
+ } catch (error1) {
+ e = error1;
+ throw new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format");
+ }
+ }
+ };
+
+ ipaddr.fromByteArray = function(bytes) {
+ var length;
+ length = bytes.length;
+ if (length === 4) {
+ return new ipaddr.IPv4(bytes);
+ } else if (length === 16) {
+ return new ipaddr.IPv6(bytes);
+ } else {
+ throw new Error("ipaddr: the binary input is neither an IPv6 nor IPv4 address");
+ }
+ };
+
+ ipaddr.process = function(string) {
+ var addr;
+ addr = this.parse(string);
+ if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) {
+ return addr.toIPv4Address();
+ } else {
+ return addr;
+ }
+ };
+
+}).call(this);
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/lib/ipaddr.js.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/lib/ipaddr.js.d.ts
new file mode 100644
index 000000000..52174b6b6
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/lib/ipaddr.js.d.ts
@@ -0,0 +1,68 @@
+declare module "ipaddr.js" {
+ type IPv4Range = 'unicast' | 'unspecified' | 'broadcast' | 'multicast' | 'linkLocal' | 'loopback' | 'carrierGradeNat' | 'private' | 'reserved';
+ type IPv6Range = 'unicast' | 'unspecified' | 'linkLocal' | 'multicast' | 'loopback' | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo' | 'reserved';
+
+ interface RangeList {
+ [name: string]: [T, number] | [T, number][];
+ }
+
+ // Common methods/properties for IPv4 and IPv6 classes.
+ class IP {
+ prefixLengthFromSubnetMask(): number | null;
+ toByteArray(): number[];
+ toNormalizedString(): string;
+ toString(): string;
+ }
+
+ namespace Address {
+ export function isValid(addr: string): boolean;
+ export function fromByteArray(bytes: number[]): IPv4 | IPv6;
+ export function parse(addr: string): IPv4 | IPv6;
+ export function parseCIDR(mask: string): [IPv4 | IPv6, number];
+ export function process(addr: string): IPv4 | IPv6;
+ export function subnetMatch(addr: IPv4, rangeList: RangeList, defaultName?: string): string;
+ export function subnetMatch(addr: IPv6, rangeList: RangeList, defaultName?: string): string;
+
+ export class IPv4 extends IP {
+ static broadcastAddressFromCIDR(addr: string): IPv4;
+ static isIPv4(addr: string): boolean;
+ static isValidFourPartDecimal(addr: string): boolean;
+ static isValid(addr: string): boolean;
+ static networkAddressFromCIDR(addr: string): IPv4;
+ static parse(addr: string): IPv4;
+ static parseCIDR(addr: string): [IPv4, number];
+ static subnetMaskFromPrefixLength(prefix: number): IPv4;
+ constructor(octets: number[]);
+ octets: number[]
+
+ kind(): 'ipv4';
+ match(addr: IPv4, bits: number): boolean;
+ match(mask: [IPv4, number]): boolean;
+ range(): IPv4Range;
+ subnetMatch(rangeList: RangeList, defaultName?: string): string;
+ toIPv4MappedAddress(): IPv6;
+ }
+
+ export class IPv6 extends IP {
+ static broadcastAddressFromCIDR(addr: string): IPv6;
+ static isIPv6(addr: string): boolean;
+ static isValid(addr: string): boolean;
+ static parse(addr: string): IPv6;
+ static parseCIDR(addr: string): [IPv6, number];
+ static subnetMaskFromPrefixLength(prefix: number): IPv6;
+ constructor(parts: number[]);
+ parts: number[]
+ zoneId?: string
+
+ isIPv4MappedAddress(): boolean;
+ kind(): 'ipv6';
+ match(addr: IPv6, bits: number): boolean;
+ match(mask: [IPv6, number]): boolean;
+ range(): IPv6Range;
+ subnetMatch(rangeList: RangeList, defaultName?: string): string;
+ toIPv4Address(): IPv4;
+ }
+ }
+
+ export = Address;
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/package.json
new file mode 100644
index 000000000..f4d35475d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/ipaddr.js/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "ipaddr.js",
+ "description": "A library for manipulating IPv4 and IPv6 addresses in JavaScript.",
+ "version": "1.9.1",
+ "author": "whitequark ",
+ "directories": {
+ "lib": "./lib"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "coffee-script": "~1.12.6",
+ "nodeunit": "^0.11.3",
+ "uglify-js": "~3.0.19"
+ },
+ "scripts": {
+ "test": "cake build test"
+ },
+ "files": [
+ "lib/",
+ "LICENSE",
+ "ipaddr.min.js"
+ ],
+ "keywords": [
+ "ip",
+ "ipv4",
+ "ipv6"
+ ],
+ "repository": "git://github.com/whitequark/ipaddr.js",
+ "main": "./lib/ipaddr.js",
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "license": "MIT",
+ "types": "./lib/ipaddr.js.d.ts"
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/.eslintrc b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/.eslintrc
new file mode 100644
index 000000000..f2e072681
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/.eslintrc
@@ -0,0 +1,18 @@
+{
+ "extends": "@ljharb",
+ "root": true,
+ "rules": {
+ "func-style": 1,
+ },
+ "overrides": [
+ {
+ "files": "test/**",
+ "rules": {
+ "global-require": 0,
+ "max-depth": 0,
+ "max-lines-per-function": 0,
+ "no-negated-condition": 0,
+ },
+ },
+ ],
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/.nycrc b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/.nycrc
new file mode 100644
index 000000000..bdd626ce9
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/.nycrc
@@ -0,0 +1,9 @@
+{
+ "all": true,
+ "check-coverage": false,
+ "reporter": ["text-summary", "text", "html", "json"],
+ "exclude": [
+ "coverage",
+ "test"
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/CHANGELOG.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/CHANGELOG.md
new file mode 100644
index 000000000..0177c82b7
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/CHANGELOG.md
@@ -0,0 +1,218 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [v2.16.1](https://github.com/inspect-js/is-core-module/compare/v2.16.0...v2.16.1) - 2024-12-21
+
+### Fixed
+
+- [Fix] `node:sqlite` is available in node ^22.13 [`#17`](https://github.com/inspect-js/is-core-module/issues/17)
+
+## [v2.16.0](https://github.com/inspect-js/is-core-module/compare/v2.15.1...v2.16.0) - 2024-12-13
+
+### Commits
+
+- [New] add `node:sqlite` [`1ee94d2`](https://github.com/inspect-js/is-core-module/commit/1ee94d20857e22cdb24e9b4bb1a2097f2e03e26f)
+- [Dev Deps] update `auto-changelog`, `tape` [`aa84aa3`](https://github.com/inspect-js/is-core-module/commit/aa84aa34face677f14e08ec1c737f0c4bba27260)
+
+## [v2.15.1](https://github.com/inspect-js/is-core-module/compare/v2.15.0...v2.15.1) - 2024-08-21
+
+### Commits
+
+- [Tests] add `process.getBuiltinModule` tests [`28c7791`](https://github.com/inspect-js/is-core-module/commit/28c7791c196d58c64cfdf638b7e68ed1b62a4da0)
+- [Fix] `test/mock_loader` is no longer exposed as of v22.7 [`68b08b0`](https://github.com/inspect-js/is-core-module/commit/68b08b0d7963447dbffa5142e8810dca550383af)
+- [Tests] replace `aud` with `npm audit` [`32f8060`](https://github.com/inspect-js/is-core-module/commit/32f806026dac14f9016be4401a643851240c76b9)
+- [Dev Deps] update `mock-property` [`f7d3c8f`](https://github.com/inspect-js/is-core-module/commit/f7d3c8f01e922be49621683eb41477c4f50522e1)
+- [Dev Deps] add missing peer dep [`eaee885`](https://github.com/inspect-js/is-core-module/commit/eaee885b67238819e9c8ed5bd2098766e1d05331)
+
+## [v2.15.0](https://github.com/inspect-js/is-core-module/compare/v2.14.0...v2.15.0) - 2024-07-17
+
+### Commits
+
+- [New] add `node:sea` [`2819fb3`](https://github.com/inspect-js/is-core-module/commit/2819fb3eae312fa64643bc5430ebd06ec0f3fb88)
+
+## [v2.14.0](https://github.com/inspect-js/is-core-module/compare/v2.13.1...v2.14.0) - 2024-06-20
+
+### Commits
+
+- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `mock-property`, `npmignore`, `tape` [`0e43200`](https://github.com/inspect-js/is-core-module/commit/0e432006d97237cc082d41e6a593e87c81068364)
+- [meta] add missing `engines.node` [`4ea3af8`](https://github.com/inspect-js/is-core-module/commit/4ea3af88891a1d4f96026f0ec0ef08c67cd1bd24)
+- [New] add `test/mock_loader` [`e9fbd29`](https://github.com/inspect-js/is-core-module/commit/e9fbd2951383be070aeffb9ebbf3715237282610)
+- [Deps] update `hasown` [`57f1940`](https://github.com/inspect-js/is-core-module/commit/57f1940947b3e368abdf529232d2f17d88909358)
+
+## [v2.13.1](https://github.com/inspect-js/is-core-module/compare/v2.13.0...v2.13.1) - 2023-10-20
+
+### Commits
+
+- [Refactor] use `hasown` instead of `has` [`0e52096`](https://github.com/inspect-js/is-core-module/commit/0e520968b0a725276b67420ab4b877486b243ae0)
+- [Dev Deps] update `mock-property`, `tape` [`8736b35`](https://github.com/inspect-js/is-core-module/commit/8736b35464d0f297b55da2c6b30deee04b8303c5)
+
+## [v2.13.0](https://github.com/inspect-js/is-core-module/compare/v2.12.1...v2.13.0) - 2023-08-05
+
+### Commits
+
+- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `semver`, `tape` [`c75b263`](https://github.com/inspect-js/is-core-module/commit/c75b263d047cb53430c3970107e5eb64d6cd6c0c)
+- [New] `node:test/reporters` and `wasi`/`node:wasi` are in v18.17 [`d76cbf8`](https://github.com/inspect-js/is-core-module/commit/d76cbf8e9b208acfd98913fed5a5f45cb15fe5dc)
+
+## [v2.12.1](https://github.com/inspect-js/is-core-module/compare/v2.12.0...v2.12.1) - 2023-05-16
+
+### Commits
+
+- [Fix] `test/reporters` now requires the `node:` prefix as of v20.2 [`12183d0`](https://github.com/inspect-js/is-core-module/commit/12183d0d8e4edf56b6ce18a1b3be54bfce10175b)
+
+## [v2.12.0](https://github.com/inspect-js/is-core-module/compare/v2.11.0...v2.12.0) - 2023-04-10
+
+### Commits
+
+- [actions] update rebase action to use reusable workflow [`c0a7251`](https://github.com/inspect-js/is-core-module/commit/c0a7251f734f3c621932c5fcdfd1bf966b42ca32)
+- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`9ae8b7f`](https://github.com/inspect-js/is-core-module/commit/9ae8b7fac03c369861d0991b4a2ce8d4848e6a7d)
+- [New] `test/reporters` added in v19.9, `wasi` added in v20 [`9d5341a`](https://github.com/inspect-js/is-core-module/commit/9d5341ab32053f25b7fa7db3c0e18461db24a79c)
+- [Dev Deps] add missing `in-publish` dep [`5980245`](https://github.com/inspect-js/is-core-module/commit/59802456e9ac919fa748f53be9d8fbf304a197df)
+
+## [v2.11.0](https://github.com/inspect-js/is-core-module/compare/v2.10.0...v2.11.0) - 2022-10-18
+
+### Commits
+
+- [meta] use `npmignore` to autogenerate an npmignore file [`3360011`](https://github.com/inspect-js/is-core-module/commit/33600118857b46177178072fba2affcdeb009d12)
+- [Dev Deps] update `aud`, `tape` [`651c6b0`](https://github.com/inspect-js/is-core-module/commit/651c6b0cc2799d4130866cf43ad333dcade3d26c)
+- [New] `inspector/promises` and `node:inspector/promises` is now available in node 19 [`22d332f`](https://github.com/inspect-js/is-core-module/commit/22d332fe22ac050305444e0781ff85af819abcb0)
+
+## [v2.10.0](https://github.com/inspect-js/is-core-module/compare/v2.9.0...v2.10.0) - 2022-08-03
+
+### Commits
+
+- [New] `node:test` is now available in node ^16.17 [`e8fd36e`](https://github.com/inspect-js/is-core-module/commit/e8fd36e9b86c917775a07cc473b62a3294f459f2)
+- [Tests] improve skip message [`c014a4c`](https://github.com/inspect-js/is-core-module/commit/c014a4c0cd6eb15fff573ae4709191775e70cab4)
+
+## [v2.9.0](https://github.com/inspect-js/is-core-module/compare/v2.8.1...v2.9.0) - 2022-04-19
+
+### Commits
+
+- [New] add `node:test`, in node 18+ [`f853eca`](https://github.com/inspect-js/is-core-module/commit/f853eca801d0a7d4e1dbb670f1b6d9837d9533c5)
+- [Tests] use `mock-property` [`03b3644`](https://github.com/inspect-js/is-core-module/commit/03b3644dff4417f4ba5a7d0aa0138f5f6b3e5c46)
+- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog`, `tape` [`7c0e2d0`](https://github.com/inspect-js/is-core-module/commit/7c0e2d06ed2a89acf53abe2ab34d703ed5b03455)
+- [meta] simplify "exports" [`d6ed201`](https://github.com/inspect-js/is-core-module/commit/d6ed201eba7fbba0e59814a9050fc49a6e9878c8)
+
+## [v2.8.1](https://github.com/inspect-js/is-core-module/compare/v2.8.0...v2.8.1) - 2022-01-05
+
+### Commits
+
+- [actions] reuse common workflows [`cd2cf9b`](https://github.com/inspect-js/is-core-module/commit/cd2cf9b3b66c8d328f65610efe41e9325db7716d)
+- [Fix] update node 0.4 results [`062195d`](https://github.com/inspect-js/is-core-module/commit/062195d89f0876a88b95d378b43f7fcc1205bc5b)
+- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `safe-publish-latest`, `tape` [`0790b62`](https://github.com/inspect-js/is-core-module/commit/0790b6222848c6167132f9f73acc3520fa8d1298)
+- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape` [`7d139a6`](https://github.com/inspect-js/is-core-module/commit/7d139a6d767709eabf0a0251e074ec1fb230c06e)
+- [Tests] run `nyc` in `tests-only`, not `test` [`780e8a0`](https://github.com/inspect-js/is-core-module/commit/780e8a049951c71cf78b1707f0871c48a28bde14)
+
+## [v2.8.0](https://github.com/inspect-js/is-core-module/compare/v2.7.0...v2.8.0) - 2021-10-14
+
+### Commits
+
+- [actions] update codecov uploader [`0cfe94e`](https://github.com/inspect-js/is-core-module/commit/0cfe94e106a7d005ea03e008c0a21dec13a77904)
+- [New] add `readline/promises` to node v17+ [`4f78c30`](https://github.com/inspect-js/is-core-module/commit/4f78c3008b1b58b4db6dc91d99610b1bc859da7e)
+- [Tests] node ^14.18 supports `node:` prefixes for CJS [`43e2f17`](https://github.com/inspect-js/is-core-module/commit/43e2f177452cea2f0eaf34f61b5407217bbdb6f4)
+
+## [v2.7.0](https://github.com/inspect-js/is-core-module/compare/v2.6.0...v2.7.0) - 2021-09-27
+
+### Commits
+
+- [New] node `v14.18` added `node:`-prefixed core modules to `require` [`6d943ab`](https://github.com/inspect-js/is-core-module/commit/6d943abe81382b9bbe344384d80fbfebe1cc0526)
+- [Tests] add coverage for Object.prototype pollution [`c6baf5f`](https://github.com/inspect-js/is-core-module/commit/c6baf5f942311a1945c1af41167bb80b84df2af7)
+- [Dev Deps] update `@ljharb/eslint-config` [`6717f00`](https://github.com/inspect-js/is-core-module/commit/6717f000d063ea57beb772bded36c2f056ac404c)
+- [eslint] fix linter warning [`594c10b`](https://github.com/inspect-js/is-core-module/commit/594c10bb7d39d7eb00925c90924199ff596184b2)
+- [meta] add `sideEffects` flag [`c32cfa5`](https://github.com/inspect-js/is-core-module/commit/c32cfa5195632944c4dd4284a142b8476e75be13)
+
+## [v2.6.0](https://github.com/inspect-js/is-core-module/compare/v2.5.0...v2.6.0) - 2021-08-17
+
+### Commits
+
+- [Dev Deps] update `eslint`, `tape` [`6cc928f`](https://github.com/inspect-js/is-core-module/commit/6cc928f8a4bba66aeeccc4f6beeac736d4bd3081)
+- [New] add `stream/consumers` to node `>= 16.7` [`a1a423e`](https://github.com/inspect-js/is-core-module/commit/a1a423e467e4cc27df180234fad5bab45943e67d)
+- [Refactor] Remove duplicated `&&` operand [`86faea7`](https://github.com/inspect-js/is-core-module/commit/86faea738213a2433c62d1098488dc9314dca832)
+- [Tests] include prereleases [`a4da7a6`](https://github.com/inspect-js/is-core-module/commit/a4da7a6abf7568e2aa4fd98e69452179f1850963)
+
+## [v2.5.0](https://github.com/inspect-js/is-core-module/compare/v2.4.0...v2.5.0) - 2021-07-12
+
+### Commits
+
+- [Dev Deps] update `auto-changelog`, `eslint` [`6334cc9`](https://github.com/inspect-js/is-core-module/commit/6334cc94f3af7469685bd8f236740991baaf2705)
+- [New] add `stream/web` to node v16.5+ [`17ac59b`](https://github.com/inspect-js/is-core-module/commit/17ac59b662d63e220a2e5728625f005c24f177b2)
+
+## [v2.4.0](https://github.com/inspect-js/is-core-module/compare/v2.3.0...v2.4.0) - 2021-05-09
+
+### Commits
+
+- [readme] add actions and codecov badges [`82b7faa`](https://github.com/inspect-js/is-core-module/commit/82b7faa12b56dbe47fbea67e1a5b9e447027ba40)
+- [Dev Deps] update `@ljharb/eslint-config`, `aud` [`8096868`](https://github.com/inspect-js/is-core-module/commit/8096868c024a161ccd4d44110b136763e92eace8)
+- [Dev Deps] update `eslint` [`6726824`](https://github.com/inspect-js/is-core-module/commit/67268249b88230018c510f6532a8046d7326346f)
+- [New] add `diagnostics_channel` to node `^14.17` [`86c6563`](https://github.com/inspect-js/is-core-module/commit/86c65634201b8ff9b3e48a9a782594579c7f5c3c)
+- [meta] fix prepublish script [`697a01e`](https://github.com/inspect-js/is-core-module/commit/697a01e3c9c0be074066520954f30fb28532ec57)
+
+## [v2.3.0](https://github.com/inspect-js/is-core-module/compare/v2.2.0...v2.3.0) - 2021-04-24
+
+### Commits
+
+- [meta] do not publish github action workflow files [`060d4bb`](https://github.com/inspect-js/is-core-module/commit/060d4bb971a29451c19ff336eb56bee27f9fa95a)
+- [New] add support for `node:` prefix, in node 16+ [`7341223`](https://github.com/inspect-js/is-core-module/commit/73412230a769f6e81c05eea50b6520cebf54ed2f)
+- [actions] use `node/install` instead of `node/run`; use `codecov` action [`016269a`](https://github.com/inspect-js/is-core-module/commit/016269abae9f6657a5254adfbb813f09a05067f9)
+- [patch] remove unneeded `.0` in version ranges [`cb466a6`](https://github.com/inspect-js/is-core-module/commit/cb466a6d89e52b8389e5c12715efcd550c41cea3)
+- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `tape` [`c9f9c39`](https://github.com/inspect-js/is-core-module/commit/c9f9c396ace60ef81906f98059c064e6452473ed)
+- [actions] update workflows [`3ee4a89`](https://github.com/inspect-js/is-core-module/commit/3ee4a89fd5a02fccd43882d905448ea6a98e9a3c)
+- [Dev Deps] update `eslint`, `@ljharb/eslint-config` [`dee4fed`](https://github.com/inspect-js/is-core-module/commit/dee4fed79690c1d43a22f7fa9426abebdc6d727f)
+- [Dev Deps] update `eslint`, `@ljharb/eslint-config` [`7d046ba`](https://github.com/inspect-js/is-core-module/commit/7d046ba07ae8c9292e43652694ca808d7b309de8)
+- [meta] use `prepublishOnly` script for npm 7+ [`149e677`](https://github.com/inspect-js/is-core-module/commit/149e6771a5ede6d097e71785b467a9c4b4977cc7)
+- [readme] remove travis badge [`903b51d`](https://github.com/inspect-js/is-core-module/commit/903b51d6b69b98abeabfbc3695c345b02646f19c)
+
+## [v2.2.0](https://github.com/inspect-js/is-core-module/compare/v2.1.0...v2.2.0) - 2020-11-26
+
+### Commits
+
+- [Tests] migrate tests to Github Actions [`c919f57`](https://github.com/inspect-js/is-core-module/commit/c919f573c0a92d10a0acad0b650b5aecb033d426)
+- [patch] `core.json`: %s/ /\t/g [`db3f685`](https://github.com/inspect-js/is-core-module/commit/db3f68581f53e73cc09cd675955eb1bdd6a5a39b)
+- [Tests] run `nyc` on all tests [`b2f925f`](https://github.com/inspect-js/is-core-module/commit/b2f925f8866f210ef441f39fcc8cc42692ab89b1)
+- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`; add `safe-publish-latest` [`89f02a2`](https://github.com/inspect-js/is-core-module/commit/89f02a2b4162246dea303a6ee31bb9a550b05c72)
+- [New] add `path/posix`, `path/win32`, `util/types` [`77f94f1`](https://github.com/inspect-js/is-core-module/commit/77f94f1e90ffd7c0be2a3f1aa8574ebf7fd981b3)
+
+## [v2.1.0](https://github.com/inspect-js/is-core-module/compare/v2.0.0...v2.1.0) - 2020-11-04
+
+### Commits
+
+- [Dev Deps] update `eslint` [`5e0034e`](https://github.com/inspect-js/is-core-module/commit/5e0034eae57c09c8f1bd769f502486a00f56c6e4)
+- [New] Add `diagnostics_channel` [`c2d83d0`](https://github.com/inspect-js/is-core-module/commit/c2d83d0a0225a1a658945d9bab7036ea347d29ec)
+
+## [v2.0.0](https://github.com/inspect-js/is-core-module/compare/v1.0.2...v2.0.0) - 2020-09-29
+
+### Commits
+
+- v2 implementation [`865aeb5`](https://github.com/inspect-js/is-core-module/commit/865aeb5ca0e90248a3dfff5d7622e4751fdeb9cd)
+- Only apps should have lockfiles [`5a5e660`](https://github.com/inspect-js/is-core-module/commit/5a5e660d568e37eb44e17fb1ebb12a105205fc2b)
+- Initial commit for v2 [`5a51524`](https://github.com/inspect-js/is-core-module/commit/5a51524e06f92adece5fbb138c69b7b9748a2348)
+- Tests [`116eae4`](https://github.com/inspect-js/is-core-module/commit/116eae4fccd01bc72c1fd3cc4b7561c387afc496)
+- [meta] add `auto-changelog` [`c24388b`](https://github.com/inspect-js/is-core-module/commit/c24388bee828d223040519d1f5b226ca35beee63)
+- [actions] add "Automatic Rebase" and "require allow edits" actions [`34292db`](https://github.com/inspect-js/is-core-module/commit/34292dbcbadae0868aff03c22dbd8b7b8a11558a)
+- [Tests] add `npm run lint` [`4f9eeee`](https://github.com/inspect-js/is-core-module/commit/4f9eeee7ddff10698bbf528620f4dc8d4fa3e697)
+- [readme] fix travis badges, https all URLs [`e516a73`](https://github.com/inspect-js/is-core-module/commit/e516a73b0dccce20938c432b1ba512eae8eff9e9)
+- [meta] create FUNDING.yml [`1aabebc`](https://github.com/inspect-js/is-core-module/commit/1aabebca98d01f8a04e46bc2e2520fa93cf21ac6)
+- [Fix] `domain`: domain landed sometime > v0.7.7 and <= v0.7.12 [`2df7d37`](https://github.com/inspect-js/is-core-module/commit/2df7d37595d41b15eeada732b706b926c2771655)
+- [Fix] `sys`: worked in 0.6, not 0.7, and 0.8+ [`a75c134`](https://github.com/inspect-js/is-core-module/commit/a75c134229e1e9441801f6b73f6a52489346eb65)
+
+## [v1.0.2](https://github.com/inspect-js/is-core-module/compare/v1.0.1...v1.0.2) - 2014-09-28
+
+### Commits
+
+- simpler [`66fe90f`](https://github.com/inspect-js/is-core-module/commit/66fe90f9771581b9adc0c3900baa52c21b5baea2)
+
+## [v1.0.1](https://github.com/inspect-js/is-core-module/compare/v1.0.0...v1.0.1) - 2014-09-28
+
+### Commits
+
+- remove stupid [`f21f906`](https://github.com/inspect-js/is-core-module/commit/f21f906f882c2bd656a5fc5ed6fbe48ddaffb2ac)
+- update readme [`1eff0ec`](https://github.com/inspect-js/is-core-module/commit/1eff0ec69798d1ec65771552d1562911e90a8027)
+
+## v1.0.0 - 2014-09-28
+
+### Commits
+
+- init [`48e5e76`](https://github.com/inspect-js/is-core-module/commit/48e5e76cac378fddb8c1f7d4055b8dfc943d6b96)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/LICENSE
new file mode 100644
index 000000000..2e502872a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Dave Justice
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/README.md
new file mode 100644
index 000000000..062d9068e
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/README.md
@@ -0,0 +1,40 @@
+# is-core-module [![Version Badge][2]][1]
+
+[![github actions][actions-image]][actions-url]
+[![coverage][codecov-image]][codecov-url]
+[![dependency status][5]][6]
+[![dev dependency status][7]][8]
+[![License][license-image]][license-url]
+[![Downloads][downloads-image]][downloads-url]
+
+[![npm badge][11]][1]
+
+Is this specifier a node.js core module? Optionally provide a node version to check; defaults to the current node version.
+
+## Example
+
+```js
+var isCore = require('is-core-module');
+var assert = require('assert');
+assert(isCore('fs'));
+assert(!isCore('butts'));
+```
+
+## Tests
+Clone the repo, `npm install`, and run `npm test`
+
+[1]: https://npmjs.org/package/is-core-module
+[2]: https://versionbadg.es/inspect-js/is-core-module.svg
+[5]: https://david-dm.org/inspect-js/is-core-module.svg
+[6]: https://david-dm.org/inspect-js/is-core-module
+[7]: https://david-dm.org/inspect-js/is-core-module/dev-status.svg
+[8]: https://david-dm.org/inspect-js/is-core-module#info=devDependencies
+[11]: https://nodei.co/npm/is-core-module.png?downloads=true&stars=true
+[license-image]: https://img.shields.io/npm/l/is-core-module.svg
+[license-url]: LICENSE
+[downloads-image]: https://img.shields.io/npm/dm/is-core-module.svg
+[downloads-url]: https://npm-stat.com/charts.html?package=is-core-module
+[codecov-image]: https://codecov.io/gh/inspect-js/is-core-module/branch/main/graphs/badge.svg
+[codecov-url]: https://app.codecov.io/gh/inspect-js/is-core-module/
+[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/inspect-js/is-core-module
+[actions-url]: https://github.com/inspect-js/is-core-module/actions
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/core.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/core.json
new file mode 100644
index 000000000..930ec6825
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/core.json
@@ -0,0 +1,162 @@
+{
+ "assert": true,
+ "node:assert": [">= 14.18 && < 15", ">= 16"],
+ "assert/strict": ">= 15",
+ "node:assert/strict": ">= 16",
+ "async_hooks": ">= 8",
+ "node:async_hooks": [">= 14.18 && < 15", ">= 16"],
+ "buffer_ieee754": ">= 0.5 && < 0.9.7",
+ "buffer": true,
+ "node:buffer": [">= 14.18 && < 15", ">= 16"],
+ "child_process": true,
+ "node:child_process": [">= 14.18 && < 15", ">= 16"],
+ "cluster": ">= 0.5",
+ "node:cluster": [">= 14.18 && < 15", ">= 16"],
+ "console": true,
+ "node:console": [">= 14.18 && < 15", ">= 16"],
+ "constants": true,
+ "node:constants": [">= 14.18 && < 15", ">= 16"],
+ "crypto": true,
+ "node:crypto": [">= 14.18 && < 15", ">= 16"],
+ "_debug_agent": ">= 1 && < 8",
+ "_debugger": "< 8",
+ "dgram": true,
+ "node:dgram": [">= 14.18 && < 15", ">= 16"],
+ "diagnostics_channel": [">= 14.17 && < 15", ">= 15.1"],
+ "node:diagnostics_channel": [">= 14.18 && < 15", ">= 16"],
+ "dns": true,
+ "node:dns": [">= 14.18 && < 15", ">= 16"],
+ "dns/promises": ">= 15",
+ "node:dns/promises": ">= 16",
+ "domain": ">= 0.7.12",
+ "node:domain": [">= 14.18 && < 15", ">= 16"],
+ "events": true,
+ "node:events": [">= 14.18 && < 15", ">= 16"],
+ "freelist": "< 6",
+ "fs": true,
+ "node:fs": [">= 14.18 && < 15", ">= 16"],
+ "fs/promises": [">= 10 && < 10.1", ">= 14"],
+ "node:fs/promises": [">= 14.18 && < 15", ">= 16"],
+ "_http_agent": ">= 0.11.1",
+ "node:_http_agent": [">= 14.18 && < 15", ">= 16"],
+ "_http_client": ">= 0.11.1",
+ "node:_http_client": [">= 14.18 && < 15", ">= 16"],
+ "_http_common": ">= 0.11.1",
+ "node:_http_common": [">= 14.18 && < 15", ">= 16"],
+ "_http_incoming": ">= 0.11.1",
+ "node:_http_incoming": [">= 14.18 && < 15", ">= 16"],
+ "_http_outgoing": ">= 0.11.1",
+ "node:_http_outgoing": [">= 14.18 && < 15", ">= 16"],
+ "_http_server": ">= 0.11.1",
+ "node:_http_server": [">= 14.18 && < 15", ">= 16"],
+ "http": true,
+ "node:http": [">= 14.18 && < 15", ">= 16"],
+ "http2": ">= 8.8",
+ "node:http2": [">= 14.18 && < 15", ">= 16"],
+ "https": true,
+ "node:https": [">= 14.18 && < 15", ">= 16"],
+ "inspector": ">= 8",
+ "node:inspector": [">= 14.18 && < 15", ">= 16"],
+ "inspector/promises": [">= 19"],
+ "node:inspector/promises": [">= 19"],
+ "_linklist": "< 8",
+ "module": true,
+ "node:module": [">= 14.18 && < 15", ">= 16"],
+ "net": true,
+ "node:net": [">= 14.18 && < 15", ">= 16"],
+ "node-inspect/lib/_inspect": ">= 7.6 && < 12",
+ "node-inspect/lib/internal/inspect_client": ">= 7.6 && < 12",
+ "node-inspect/lib/internal/inspect_repl": ">= 7.6 && < 12",
+ "os": true,
+ "node:os": [">= 14.18 && < 15", ">= 16"],
+ "path": true,
+ "node:path": [">= 14.18 && < 15", ">= 16"],
+ "path/posix": ">= 15.3",
+ "node:path/posix": ">= 16",
+ "path/win32": ">= 15.3",
+ "node:path/win32": ">= 16",
+ "perf_hooks": ">= 8.5",
+ "node:perf_hooks": [">= 14.18 && < 15", ">= 16"],
+ "process": ">= 1",
+ "node:process": [">= 14.18 && < 15", ">= 16"],
+ "punycode": ">= 0.5",
+ "node:punycode": [">= 14.18 && < 15", ">= 16"],
+ "querystring": true,
+ "node:querystring": [">= 14.18 && < 15", ">= 16"],
+ "readline": true,
+ "node:readline": [">= 14.18 && < 15", ">= 16"],
+ "readline/promises": ">= 17",
+ "node:readline/promises": ">= 17",
+ "repl": true,
+ "node:repl": [">= 14.18 && < 15", ">= 16"],
+ "node:sea": [">= 20.12 && < 21", ">= 21.7"],
+ "smalloc": ">= 0.11.5 && < 3",
+ "node:sqlite": [">= 22.13 && < 23", ">= 23.4"],
+ "_stream_duplex": ">= 0.9.4",
+ "node:_stream_duplex": [">= 14.18 && < 15", ">= 16"],
+ "_stream_transform": ">= 0.9.4",
+ "node:_stream_transform": [">= 14.18 && < 15", ">= 16"],
+ "_stream_wrap": ">= 1.4.1",
+ "node:_stream_wrap": [">= 14.18 && < 15", ">= 16"],
+ "_stream_passthrough": ">= 0.9.4",
+ "node:_stream_passthrough": [">= 14.18 && < 15", ">= 16"],
+ "_stream_readable": ">= 0.9.4",
+ "node:_stream_readable": [">= 14.18 && < 15", ">= 16"],
+ "_stream_writable": ">= 0.9.4",
+ "node:_stream_writable": [">= 14.18 && < 15", ">= 16"],
+ "stream": true,
+ "node:stream": [">= 14.18 && < 15", ">= 16"],
+ "stream/consumers": ">= 16.7",
+ "node:stream/consumers": ">= 16.7",
+ "stream/promises": ">= 15",
+ "node:stream/promises": ">= 16",
+ "stream/web": ">= 16.5",
+ "node:stream/web": ">= 16.5",
+ "string_decoder": true,
+ "node:string_decoder": [">= 14.18 && < 15", ">= 16"],
+ "sys": [">= 0.4 && < 0.7", ">= 0.8"],
+ "node:sys": [">= 14.18 && < 15", ">= 16"],
+ "test/reporters": ">= 19.9 && < 20.2",
+ "node:test/reporters": [">= 18.17 && < 19", ">= 19.9", ">= 20"],
+ "test/mock_loader": ">= 22.3 && < 22.7",
+ "node:test/mock_loader": ">= 22.3 && < 22.7",
+ "node:test": [">= 16.17 && < 17", ">= 18"],
+ "timers": true,
+ "node:timers": [">= 14.18 && < 15", ">= 16"],
+ "timers/promises": ">= 15",
+ "node:timers/promises": ">= 16",
+ "_tls_common": ">= 0.11.13",
+ "node:_tls_common": [">= 14.18 && < 15", ">= 16"],
+ "_tls_legacy": ">= 0.11.3 && < 10",
+ "_tls_wrap": ">= 0.11.3",
+ "node:_tls_wrap": [">= 14.18 && < 15", ">= 16"],
+ "tls": true,
+ "node:tls": [">= 14.18 && < 15", ">= 16"],
+ "trace_events": ">= 10",
+ "node:trace_events": [">= 14.18 && < 15", ">= 16"],
+ "tty": true,
+ "node:tty": [">= 14.18 && < 15", ">= 16"],
+ "url": true,
+ "node:url": [">= 14.18 && < 15", ">= 16"],
+ "util": true,
+ "node:util": [">= 14.18 && < 15", ">= 16"],
+ "util/types": ">= 15.3",
+ "node:util/types": ">= 16",
+ "v8/tools/arguments": ">= 10 && < 12",
+ "v8/tools/codemap": [">= 4.4 && < 5", ">= 5.2 && < 12"],
+ "v8/tools/consarray": [">= 4.4 && < 5", ">= 5.2 && < 12"],
+ "v8/tools/csvparser": [">= 4.4 && < 5", ">= 5.2 && < 12"],
+ "v8/tools/logreader": [">= 4.4 && < 5", ">= 5.2 && < 12"],
+ "v8/tools/profile_view": [">= 4.4 && < 5", ">= 5.2 && < 12"],
+ "v8/tools/splaytree": [">= 4.4 && < 5", ">= 5.2 && < 12"],
+ "v8": ">= 1",
+ "node:v8": [">= 14.18 && < 15", ">= 16"],
+ "vm": true,
+ "node:vm": [">= 14.18 && < 15", ">= 16"],
+ "wasi": [">= 13.4 && < 13.5", ">= 18.17 && < 19", ">= 20"],
+ "node:wasi": [">= 18.17 && < 19", ">= 20"],
+ "worker_threads": ">= 11.7",
+ "node:worker_threads": [">= 14.18 && < 15", ">= 16"],
+ "zlib": ">= 0.5",
+ "node:zlib": [">= 14.18 && < 15", ">= 16"]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/index.js
new file mode 100644
index 000000000..423e20c0d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/index.js
@@ -0,0 +1,69 @@
+'use strict';
+
+var hasOwn = require('hasown');
+
+function specifierIncluded(current, specifier) {
+ var nodeParts = current.split('.');
+ var parts = specifier.split(' ');
+ var op = parts.length > 1 ? parts[0] : '=';
+ var versionParts = (parts.length > 1 ? parts[1] : parts[0]).split('.');
+
+ for (var i = 0; i < 3; ++i) {
+ var cur = parseInt(nodeParts[i] || 0, 10);
+ var ver = parseInt(versionParts[i] || 0, 10);
+ if (cur === ver) {
+ continue; // eslint-disable-line no-restricted-syntax, no-continue
+ }
+ if (op === '<') {
+ return cur < ver;
+ }
+ if (op === '>=') {
+ return cur >= ver;
+ }
+ return false;
+ }
+ return op === '>=';
+}
+
+function matchesRange(current, range) {
+ var specifiers = range.split(/ ?&& ?/);
+ if (specifiers.length === 0) {
+ return false;
+ }
+ for (var i = 0; i < specifiers.length; ++i) {
+ if (!specifierIncluded(current, specifiers[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function versionIncluded(nodeVersion, specifierValue) {
+ if (typeof specifierValue === 'boolean') {
+ return specifierValue;
+ }
+
+ var current = typeof nodeVersion === 'undefined'
+ ? process.versions && process.versions.node
+ : nodeVersion;
+
+ if (typeof current !== 'string') {
+ throw new TypeError(typeof nodeVersion === 'undefined' ? 'Unable to determine current node version' : 'If provided, a valid node version is required');
+ }
+
+ if (specifierValue && typeof specifierValue === 'object') {
+ for (var i = 0; i < specifierValue.length; ++i) {
+ if (matchesRange(current, specifierValue[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return matchesRange(current, specifierValue);
+}
+
+var data = require('./core.json');
+
+module.exports = function isCore(x, nodeVersion) {
+ return hasOwn(data, x) && versionIncluded(nodeVersion, data[x]);
+};
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/package.json
new file mode 100644
index 000000000..266825646
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/package.json
@@ -0,0 +1,76 @@
+{
+ "name": "is-core-module",
+ "version": "2.16.1",
+ "description": "Is this specifier a node.js core module?",
+ "main": "index.js",
+ "sideEffects": false,
+ "exports": {
+ ".": "./index.js",
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "prepack": "npmignore --auto --commentLines=autogenerated",
+ "prepublish": "not-in-publish || npm run prepublishOnly",
+ "prepublishOnly": "safe-publish-latest",
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "tests-only": "nyc tape 'test/**/*.js'",
+ "test": "npm run tests-only",
+ "posttest": "npx npm@'>=10.2' audit --production",
+ "version": "auto-changelog && git add CHANGELOG.md",
+ "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/inspect-js/is-core-module.git"
+ },
+ "keywords": [
+ "core",
+ "modules",
+ "module",
+ "npm",
+ "node",
+ "dependencies"
+ ],
+ "author": "Jordan Harband ",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/inspect-js/is-core-module/issues"
+ },
+ "homepage": "https://github.com/inspect-js/is-core-module",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "devDependencies": {
+ "@ljharb/eslint-config": "^21.1.1",
+ "auto-changelog": "^2.5.0",
+ "encoding": "^0.1.13",
+ "eslint": "=8.8.0",
+ "in-publish": "^2.0.1",
+ "mock-property": "^1.1.0",
+ "npmignore": "^0.3.1",
+ "nyc": "^10.3.2",
+ "safe-publish-latest": "^2.0.0",
+ "semver": "^6.3.1",
+ "tape": "^5.9.0"
+ },
+ "auto-changelog": {
+ "output": "CHANGELOG.md",
+ "template": "keepachangelog",
+ "unreleased": false,
+ "commitLimit": false,
+ "backfillLimit": false,
+ "hideCredit": true
+ },
+ "publishConfig": {
+ "ignore": [
+ ".github"
+ ]
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/test/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/test/index.js
new file mode 100644
index 000000000..7a81e1c7e
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-core-module/test/index.js
@@ -0,0 +1,157 @@
+'use strict';
+
+var test = require('tape');
+var keys = require('object-keys');
+var semver = require('semver');
+var mockProperty = require('mock-property');
+
+var isCore = require('../');
+var data = require('../core.json');
+
+var supportsNodePrefix = semver.satisfies(process.versions.node, '^14.18 || >= 16', { includePrerelease: true });
+
+test('core modules', function (t) {
+ t.test('isCore()', function (st) {
+ st.ok(isCore('fs'));
+ st.ok(isCore('net'));
+ st.ok(isCore('http'));
+
+ st.ok(!isCore('seq'));
+ st.ok(!isCore('../'));
+
+ st.ok(!isCore('toString'));
+
+ st.end();
+ });
+
+ t.test('core list', function (st) {
+ var cores = keys(data);
+ st.plan(cores.length);
+
+ for (var i = 0; i < cores.length; ++i) {
+ var mod = cores[i];
+ var requireFunc = function () { require(mod); }; // eslint-disable-line no-loop-func
+ if (isCore(mod)) {
+ st.doesNotThrow(requireFunc, mod + ' supported; requiring does not throw');
+ } else {
+ st['throws'](requireFunc, mod + ' not supported; requiring throws');
+ }
+ }
+
+ st.end();
+ });
+
+ t.test('core via repl module', { skip: !data.repl }, function (st) {
+ var libs = require('repl')._builtinLibs; // eslint-disable-line no-underscore-dangle
+ if (!libs) {
+ st.skip('repl._builtinLibs does not exist');
+ } else {
+ for (var i = 0; i < libs.length; ++i) {
+ var mod = libs[i];
+ st.ok(data[mod], mod + ' is a core module');
+ st.doesNotThrow(
+ function () { require(mod); }, // eslint-disable-line no-loop-func
+ 'requiring ' + mod + ' does not throw'
+ );
+ if (mod.slice(0, 5) !== 'node:') {
+ if (supportsNodePrefix) {
+ st.doesNotThrow(
+ function () { require('node:' + mod); }, // eslint-disable-line no-loop-func
+ 'requiring node:' + mod + ' does not throw'
+ );
+ } else {
+ st['throws'](
+ function () { require('node:' + mod); }, // eslint-disable-line no-loop-func
+ 'requiring node:' + mod + ' throws'
+ );
+ }
+ }
+ }
+ }
+ st.end();
+ });
+
+ t.test('core via builtinModules list', { skip: !data.module }, function (st) {
+ var Module = require('module');
+ var libs = Module.builtinModules;
+ if (!libs) {
+ st.skip('module.builtinModules does not exist');
+ } else {
+ var excludeList = [
+ '_debug_agent',
+ 'v8/tools/tickprocessor-driver',
+ 'v8/tools/SourceMap',
+ 'v8/tools/tickprocessor',
+ 'v8/tools/profile'
+ ];
+
+ // see https://github.com/nodejs/node/issues/42785
+ if (semver.satisfies(process.version, '>= 18')) {
+ libs = libs.concat('node:test');
+ }
+ if (semver.satisfies(process.version, '^20.12 || >= 21.7')) {
+ libs = libs.concat('node:sea');
+ }
+ if (semver.satisfies(process.version, '>= 23.4')) {
+ libs = libs.concat('node:sqlite');
+ }
+
+ for (var i = 0; i < libs.length; ++i) {
+ var mod = libs[i];
+ if (excludeList.indexOf(mod) === -1) {
+ st.ok(data[mod], mod + ' is a core module');
+
+ if (Module.isBuiltin) {
+ st.ok(Module.isBuiltin(mod), 'module.isBuiltin(' + mod + ') is true');
+ }
+
+ st.doesNotThrow(
+ function () { require(mod); }, // eslint-disable-line no-loop-func
+ 'requiring ' + mod + ' does not throw'
+ );
+
+ if (process.getBuiltinModule) {
+ st.equal(
+ process.getBuiltinModule(mod),
+ require(mod),
+ 'process.getBuiltinModule(' + mod + ') === require(' + mod + ')'
+ );
+ }
+
+ if (mod.slice(0, 5) !== 'node:') {
+ if (supportsNodePrefix) {
+ st.doesNotThrow(
+ function () { require('node:' + mod); }, // eslint-disable-line no-loop-func
+ 'requiring node:' + mod + ' does not throw'
+ );
+ } else {
+ st['throws'](
+ function () { require('node:' + mod); }, // eslint-disable-line no-loop-func
+ 'requiring node:' + mod + ' throws'
+ );
+ }
+ }
+ }
+ }
+ }
+
+ st.end();
+ });
+
+ t.test('Object.prototype pollution', function (st) {
+ var nonKey = 'not a core module';
+ st.teardown(mockProperty(Object.prototype, 'fs', { value: false }));
+ st.teardown(mockProperty(Object.prototype, 'path', { value: '>= 999999999' }));
+ st.teardown(mockProperty(Object.prototype, 'http', { value: data.http }));
+ st.teardown(mockProperty(Object.prototype, nonKey, { value: true }));
+
+ st.equal(isCore('fs'), true, 'fs is a core module even if Object.prototype lies');
+ st.equal(isCore('path'), true, 'path is a core module even if Object.prototype lies');
+ st.equal(isCore('http'), true, 'path is a core module even if Object.prototype matches data');
+ st.equal(isCore(nonKey), false, '"' + nonKey + '" is not a core module even if Object.prototype lies');
+
+ st.end();
+ });
+
+ t.end();
+});
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/index.d.ts
new file mode 100644
index 000000000..729d20205
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/index.d.ts
@@ -0,0 +1,17 @@
+/**
+Check if the character represented by a given [Unicode code point](https://en.wikipedia.org/wiki/Code_point) is [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms).
+
+@param codePoint - The [code point](https://en.wikipedia.org/wiki/Code_point) of a character.
+
+@example
+```
+import isFullwidthCodePoint from 'is-fullwidth-code-point';
+
+isFullwidthCodePoint('谢'.codePointAt(0));
+//=> true
+
+isFullwidthCodePoint('a'.codePointAt(0));
+//=> false
+```
+*/
+export default function isFullwidthCodePoint(codePoint: number): boolean;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/index.js
new file mode 100644
index 000000000..671f97f76
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/index.js
@@ -0,0 +1,50 @@
+/* eslint-disable yoda */
+'use strict';
+
+const isFullwidthCodePoint = codePoint => {
+ if (Number.isNaN(codePoint)) {
+ return false;
+ }
+
+ // Code points are derived from:
+ // http://www.unix.org/Public/UNIDATA/EastAsianWidth.txt
+ if (
+ codePoint >= 0x1100 && (
+ codePoint <= 0x115F || // Hangul Jamo
+ codePoint === 0x2329 || // LEFT-POINTING ANGLE BRACKET
+ codePoint === 0x232A || // RIGHT-POINTING ANGLE BRACKET
+ // CJK Radicals Supplement .. Enclosed CJK Letters and Months
+ (0x2E80 <= codePoint && codePoint <= 0x3247 && codePoint !== 0x303F) ||
+ // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
+ (0x3250 <= codePoint && codePoint <= 0x4DBF) ||
+ // CJK Unified Ideographs .. Yi Radicals
+ (0x4E00 <= codePoint && codePoint <= 0xA4C6) ||
+ // Hangul Jamo Extended-A
+ (0xA960 <= codePoint && codePoint <= 0xA97C) ||
+ // Hangul Syllables
+ (0xAC00 <= codePoint && codePoint <= 0xD7A3) ||
+ // CJK Compatibility Ideographs
+ (0xF900 <= codePoint && codePoint <= 0xFAFF) ||
+ // Vertical Forms
+ (0xFE10 <= codePoint && codePoint <= 0xFE19) ||
+ // CJK Compatibility Forms .. Small Form Variants
+ (0xFE30 <= codePoint && codePoint <= 0xFE6B) ||
+ // Halfwidth and Fullwidth Forms
+ (0xFF01 <= codePoint && codePoint <= 0xFF60) ||
+ (0xFFE0 <= codePoint && codePoint <= 0xFFE6) ||
+ // Kana Supplement
+ (0x1B000 <= codePoint && codePoint <= 0x1B001) ||
+ // Enclosed Ideographic Supplement
+ (0x1F200 <= codePoint && codePoint <= 0x1F251) ||
+ // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
+ (0x20000 <= codePoint && codePoint <= 0x3FFFD)
+ )
+ ) {
+ return true;
+ }
+
+ return false;
+};
+
+module.exports = isFullwidthCodePoint;
+module.exports.default = isFullwidthCodePoint;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/license b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/license
new file mode 100644
index 000000000..e7af2f771
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/license
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/package.json
new file mode 100644
index 000000000..2137e888f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "is-fullwidth-code-point",
+ "version": "3.0.0",
+ "description": "Check if the character represented by a given Unicode code point is fullwidth",
+ "license": "MIT",
+ "repository": "sindresorhus/is-fullwidth-code-point",
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "scripts": {
+ "test": "xo && ava && tsd-check"
+ },
+ "files": [
+ "index.js",
+ "index.d.ts"
+ ],
+ "keywords": [
+ "fullwidth",
+ "full-width",
+ "full",
+ "width",
+ "unicode",
+ "character",
+ "string",
+ "codepoint",
+ "code",
+ "point",
+ "is",
+ "detect",
+ "check"
+ ],
+ "devDependencies": {
+ "ava": "^1.3.1",
+ "tsd-check": "^0.5.0",
+ "xo": "^0.24.0"
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/readme.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/readme.md
new file mode 100644
index 000000000..4236bba98
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-fullwidth-code-point/readme.md
@@ -0,0 +1,39 @@
+# is-fullwidth-code-point [](https://travis-ci.org/sindresorhus/is-fullwidth-code-point)
+
+> Check if the character represented by a given [Unicode code point](https://en.wikipedia.org/wiki/Code_point) is [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms)
+
+
+## Install
+
+```
+$ npm install is-fullwidth-code-point
+```
+
+
+## Usage
+
+```js
+const isFullwidthCodePoint = require('is-fullwidth-code-point');
+
+isFullwidthCodePoint('谢'.codePointAt(0));
+//=> true
+
+isFullwidthCodePoint('a'.codePointAt(0));
+//=> false
+```
+
+
+## API
+
+### isFullwidthCodePoint(codePoint)
+
+#### codePoint
+
+Type: `number`
+
+The [code point](https://en.wikipedia.org/wiki/Code_point) of a character.
+
+
+## License
+
+MIT © [Sindre Sorhus](https://sindresorhus.com)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/.npmignore b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/.npmignore
new file mode 100644
index 000000000..3c3629e64
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/.npmignore
@@ -0,0 +1 @@
+node_modules
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/.travis.yml b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/.travis.yml
new file mode 100644
index 000000000..03dcca57b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/.travis.yml
@@ -0,0 +1,8 @@
+language: node_js
+node_js:
+- '7'
+- '6'
+- '5'
+- '4'
+- '0.12'
+- '0.10'
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/LICENSE
new file mode 100644
index 000000000..4a59c9417
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Thomas Watson Steen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/README.md
new file mode 100644
index 000000000..31a8f566c
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/README.md
@@ -0,0 +1,27 @@
+# is-lambda
+
+Returns `true` if the current environment is an [AWS
+Lambda](https://aws.amazon.com/lambda/) server.
+
+[](https://travis-ci.org/watson/is-lambda)
+[](https://github.com/feross/standard)
+
+## Installation
+
+```
+npm install is-lambda
+```
+
+## Usage
+
+```js
+var isLambda = require('is-lambda')
+
+if (isLambda) {
+ console.log('The code is running on a AWS Lambda')
+}
+```
+
+## License
+
+MIT
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/index.js
new file mode 100644
index 000000000..b245ab1c6
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/index.js
@@ -0,0 +1,6 @@
+'use strict'
+
+module.exports = !!(
+ (process.env.LAMBDA_TASK_ROOT && process.env.AWS_EXECUTION_ENV) ||
+ false
+)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/package.json
new file mode 100644
index 000000000..d8550898b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "is-lambda",
+ "version": "1.0.1",
+ "description": "Detect if your code is running on an AWS Lambda server",
+ "main": "index.js",
+ "dependencies": {},
+ "devDependencies": {
+ "clear-require": "^1.0.1",
+ "standard": "^10.0.2"
+ },
+ "scripts": {
+ "test": "standard && node test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/watson/is-lambda.git"
+ },
+ "keywords": [
+ "aws",
+ "hosting",
+ "hosted",
+ "lambda",
+ "detect"
+ ],
+ "author": "Thomas Watson Steen (https://twitter.com/wa7son)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/watson/is-lambda/issues"
+ },
+ "homepage": "https://github.com/watson/is-lambda",
+ "coordinates": [
+ 37.3859955,
+ -122.0838831
+ ]
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/test.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/test.js
new file mode 100644
index 000000000..e8e73257a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-lambda/test.js
@@ -0,0 +1,16 @@
+'use strict'
+
+var assert = require('assert')
+var clearRequire = require('clear-require')
+
+process.env.AWS_EXECUTION_ENV = 'AWS_Lambda_nodejs6.10'
+process.env.LAMBDA_TASK_ROOT = '/var/task'
+
+var isCI = require('./')
+assert(isCI)
+
+delete process.env.AWS_EXECUTION_ENV
+
+clearRequire('./')
+isCI = require('./')
+assert(!isCI)
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/LICENSE
new file mode 100644
index 000000000..27cc9f377
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.d.ts b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.d.ts
new file mode 100644
index 000000000..2107b4289
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.d.ts
@@ -0,0 +1,2 @@
+declare function isPromise(obj: PromiseLike | S): obj is PromiseLike;
+export default isPromise;
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.js
new file mode 100644
index 000000000..1bed0871b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.js
@@ -0,0 +1,6 @@
+module.exports = isPromise;
+module.exports.default = isPromise;
+
+function isPromise(obj) {
+ return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.mjs b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.mjs
new file mode 100644
index 000000000..bf9e99b5c
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/index.mjs
@@ -0,0 +1,3 @@
+export default function isPromise(obj) {
+ return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/package.json
new file mode 100644
index 000000000..2a3c5404a
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "is-promise",
+ "version": "4.0.0",
+ "description": "Test whether an object looks like a promises-a+ promise",
+ "main": "./index.js",
+ "scripts": {
+ "test": "node test"
+ },
+ "files": [
+ "index.js",
+ "index.mjs",
+ "index.d.ts"
+ ],
+ "exports": {
+ ".": [
+ {
+ "import": "./index.mjs",
+ "require": "./index.js",
+ "default": "./index.js"
+ },
+ "./index.js"
+ ]
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/then/is-promise.git"
+ },
+ "author": "ForbesLindesay",
+ "license": "MIT"
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/readme.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/readme.md
new file mode 100644
index 000000000..d53d34bd5
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/is-promise/readme.md
@@ -0,0 +1,33 @@
+
+
+# is-promise
+
+ Test whether an object looks like a promises-a+ promise
+
+ [](https://travis-ci.org/then/is-promise)
+ [](https://david-dm.org/then/is-promise)
+ [](https://www.npmjs.org/package/is-promise)
+
+
+
+## Installation
+
+ $ npm install is-promise
+
+You can also use it client side via npm.
+
+## API
+
+```typescript
+import isPromise from 'is-promise';
+
+isPromise(Promise.resolve());//=>true
+isPromise({then:function () {...}});//=>true
+isPromise(null);//=>false
+isPromise({});//=>false
+isPromise({then: true})//=>false
+```
+
+## License
+
+ MIT
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/.npmignore b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/.npmignore
new file mode 100644
index 000000000..c1cb757ac
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/.npmignore
@@ -0,0 +1,2 @@
+.nyc_output/
+coverage/
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/LICENSE
new file mode 100644
index 000000000..19129e315
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/README.md
new file mode 100644
index 000000000..35769e844
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/README.md
@@ -0,0 +1,51 @@
+# isexe
+
+Minimal module to check if a file is executable, and a normal file.
+
+Uses `fs.stat` and tests against the `PATHEXT` environment variable on
+Windows.
+
+## USAGE
+
+```javascript
+var isexe = require('isexe')
+isexe('some-file-name', function (err, isExe) {
+ if (err) {
+ console.error('probably file does not exist or something', err)
+ } else if (isExe) {
+ console.error('this thing can be run')
+ } else {
+ console.error('cannot be run')
+ }
+})
+
+// same thing but synchronous, throws errors
+var isExe = isexe.sync('some-file-name')
+
+// treat errors as just "not executable"
+isexe('maybe-missing-file', { ignoreErrors: true }, callback)
+var isExe = isexe.sync('maybe-missing-file', { ignoreErrors: true })
+```
+
+## API
+
+### `isexe(path, [options], [callback])`
+
+Check if the path is executable. If no callback provided, and a
+global `Promise` object is available, then a Promise will be returned.
+
+Will raise whatever errors may be raised by `fs.stat`, unless
+`options.ignoreErrors` is set to true.
+
+### `isexe.sync(path, [options])`
+
+Same as `isexe` but returns the value and throws any errors raised.
+
+### Options
+
+* `ignoreErrors` Treat all errors as "no, this is not executable", but
+ don't raise them.
+* `uid` Number to use as the user id
+* `gid` Number to use as the group id
+* `pathExt` List of path extensions to use instead of `PATHEXT`
+ environment variable on Windows.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/index.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/index.js
new file mode 100644
index 000000000..553fb32b1
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/index.js
@@ -0,0 +1,57 @@
+var fs = require('fs')
+var core
+if (process.platform === 'win32' || global.TESTING_WINDOWS) {
+ core = require('./windows.js')
+} else {
+ core = require('./mode.js')
+}
+
+module.exports = isexe
+isexe.sync = sync
+
+function isexe (path, options, cb) {
+ if (typeof options === 'function') {
+ cb = options
+ options = {}
+ }
+
+ if (!cb) {
+ if (typeof Promise !== 'function') {
+ throw new TypeError('callback not provided')
+ }
+
+ return new Promise(function (resolve, reject) {
+ isexe(path, options || {}, function (er, is) {
+ if (er) {
+ reject(er)
+ } else {
+ resolve(is)
+ }
+ })
+ })
+ }
+
+ core(path, options || {}, function (er, is) {
+ // ignore EACCES because that just means we aren't allowed to run it
+ if (er) {
+ if (er.code === 'EACCES' || options && options.ignoreErrors) {
+ er = null
+ is = false
+ }
+ }
+ cb(er, is)
+ })
+}
+
+function sync (path, options) {
+ // my kingdom for a filtered catch
+ try {
+ return core.sync(path, options || {})
+ } catch (er) {
+ if (options && options.ignoreErrors || er.code === 'EACCES') {
+ return false
+ } else {
+ throw er
+ }
+ }
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/mode.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/mode.js
new file mode 100644
index 000000000..1995ea4a0
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/mode.js
@@ -0,0 +1,41 @@
+module.exports = isexe
+isexe.sync = sync
+
+var fs = require('fs')
+
+function isexe (path, options, cb) {
+ fs.stat(path, function (er, stat) {
+ cb(er, er ? false : checkStat(stat, options))
+ })
+}
+
+function sync (path, options) {
+ return checkStat(fs.statSync(path), options)
+}
+
+function checkStat (stat, options) {
+ return stat.isFile() && checkMode(stat, options)
+}
+
+function checkMode (stat, options) {
+ var mod = stat.mode
+ var uid = stat.uid
+ var gid = stat.gid
+
+ var myUid = options.uid !== undefined ?
+ options.uid : process.getuid && process.getuid()
+ var myGid = options.gid !== undefined ?
+ options.gid : process.getgid && process.getgid()
+
+ var u = parseInt('100', 8)
+ var g = parseInt('010', 8)
+ var o = parseInt('001', 8)
+ var ug = u | g
+
+ var ret = (mod & o) ||
+ (mod & g) && gid === myGid ||
+ (mod & u) && uid === myUid ||
+ (mod & ug) && myUid === 0
+
+ return ret
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/package.json b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/package.json
new file mode 100644
index 000000000..e45268944
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "isexe",
+ "version": "2.0.0",
+ "description": "Minimal module to check if a file is executable.",
+ "main": "index.js",
+ "directories": {
+ "test": "test"
+ },
+ "devDependencies": {
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.0",
+ "tap": "^10.3.0"
+ },
+ "scripts": {
+ "test": "tap test/*.js --100",
+ "preversion": "npm test",
+ "postversion": "npm publish",
+ "postpublish": "git push origin --all; git push origin --tags"
+ },
+ "author": "Isaac Z. Schlueter (http://blog.izs.me/)",
+ "license": "ISC",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/isaacs/isexe.git"
+ },
+ "keywords": [],
+ "bugs": {
+ "url": "https://github.com/isaacs/isexe/issues"
+ },
+ "homepage": "https://github.com/isaacs/isexe#readme"
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/test/basic.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/test/basic.js
new file mode 100644
index 000000000..d926df64b
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/test/basic.js
@@ -0,0 +1,221 @@
+var t = require('tap')
+var fs = require('fs')
+var path = require('path')
+var fixture = path.resolve(__dirname, 'fixtures')
+var meow = fixture + '/meow.cat'
+var mine = fixture + '/mine.cat'
+var ours = fixture + '/ours.cat'
+var fail = fixture + '/fail.false'
+var noent = fixture + '/enoent.exe'
+var mkdirp = require('mkdirp')
+var rimraf = require('rimraf')
+
+var isWindows = process.platform === 'win32'
+var hasAccess = typeof fs.access === 'function'
+var winSkip = isWindows && 'windows'
+var accessSkip = !hasAccess && 'no fs.access function'
+var hasPromise = typeof Promise === 'function'
+var promiseSkip = !hasPromise && 'no global Promise'
+
+function reset () {
+ delete require.cache[require.resolve('../')]
+ return require('../')
+}
+
+t.test('setup fixtures', function (t) {
+ rimraf.sync(fixture)
+ mkdirp.sync(fixture)
+ fs.writeFileSync(meow, '#!/usr/bin/env cat\nmeow\n')
+ fs.chmodSync(meow, parseInt('0755', 8))
+ fs.writeFileSync(fail, '#!/usr/bin/env false\n')
+ fs.chmodSync(fail, parseInt('0644', 8))
+ fs.writeFileSync(mine, '#!/usr/bin/env cat\nmine\n')
+ fs.chmodSync(mine, parseInt('0744', 8))
+ fs.writeFileSync(ours, '#!/usr/bin/env cat\nours\n')
+ fs.chmodSync(ours, parseInt('0754', 8))
+ t.end()
+})
+
+t.test('promise', { skip: promiseSkip }, function (t) {
+ var isexe = reset()
+ t.test('meow async', function (t) {
+ isexe(meow).then(function (is) {
+ t.ok(is)
+ t.end()
+ })
+ })
+ t.test('fail async', function (t) {
+ isexe(fail).then(function (is) {
+ t.notOk(is)
+ t.end()
+ })
+ })
+ t.test('noent async', function (t) {
+ isexe(noent).catch(function (er) {
+ t.ok(er)
+ t.end()
+ })
+ })
+ t.test('noent ignore async', function (t) {
+ isexe(noent, { ignoreErrors: true }).then(function (is) {
+ t.notOk(is)
+ t.end()
+ })
+ })
+ t.end()
+})
+
+t.test('no promise', function (t) {
+ global.Promise = null
+ var isexe = reset()
+ t.throws('try to meow a promise', function () {
+ isexe(meow)
+ })
+ t.end()
+})
+
+t.test('access', { skip: accessSkip || winSkip }, function (t) {
+ runTest(t)
+})
+
+t.test('mode', { skip: winSkip }, function (t) {
+ delete fs.access
+ delete fs.accessSync
+ var isexe = reset()
+ t.ok(isexe.sync(ours, { uid: 0, gid: 0 }))
+ t.ok(isexe.sync(mine, { uid: 0, gid: 0 }))
+ runTest(t)
+})
+
+t.test('windows', function (t) {
+ global.TESTING_WINDOWS = true
+ var pathExt = '.EXE;.CAT;.CMD;.COM'
+ t.test('pathExt option', function (t) {
+ runTest(t, { pathExt: '.EXE;.CAT;.CMD;.COM' })
+ })
+ t.test('pathExt env', function (t) {
+ process.env.PATHEXT = pathExt
+ runTest(t)
+ })
+ t.test('no pathExt', function (t) {
+ // with a pathExt of '', any filename is fine.
+ // so the "fail" one would still pass.
+ runTest(t, { pathExt: '', skipFail: true })
+ })
+ t.test('pathext with empty entry', function (t) {
+ // with a pathExt of '', any filename is fine.
+ // so the "fail" one would still pass.
+ runTest(t, { pathExt: ';' + pathExt, skipFail: true })
+ })
+ t.end()
+})
+
+t.test('cleanup', function (t) {
+ rimraf.sync(fixture)
+ t.end()
+})
+
+function runTest (t, options) {
+ var isexe = reset()
+
+ var optionsIgnore = Object.create(options || {})
+ optionsIgnore.ignoreErrors = true
+
+ if (!options || !options.skipFail) {
+ t.notOk(isexe.sync(fail, options))
+ }
+ t.notOk(isexe.sync(noent, optionsIgnore))
+ if (!options) {
+ t.ok(isexe.sync(meow))
+ } else {
+ t.ok(isexe.sync(meow, options))
+ }
+
+ t.ok(isexe.sync(mine, options))
+ t.ok(isexe.sync(ours, options))
+ t.throws(function () {
+ isexe.sync(noent, options)
+ })
+
+ t.test('meow async', function (t) {
+ if (!options) {
+ isexe(meow, function (er, is) {
+ if (er) {
+ throw er
+ }
+ t.ok(is)
+ t.end()
+ })
+ } else {
+ isexe(meow, options, function (er, is) {
+ if (er) {
+ throw er
+ }
+ t.ok(is)
+ t.end()
+ })
+ }
+ })
+
+ t.test('mine async', function (t) {
+ isexe(mine, options, function (er, is) {
+ if (er) {
+ throw er
+ }
+ t.ok(is)
+ t.end()
+ })
+ })
+
+ t.test('ours async', function (t) {
+ isexe(ours, options, function (er, is) {
+ if (er) {
+ throw er
+ }
+ t.ok(is)
+ t.end()
+ })
+ })
+
+ if (!options || !options.skipFail) {
+ t.test('fail async', function (t) {
+ isexe(fail, options, function (er, is) {
+ if (er) {
+ throw er
+ }
+ t.notOk(is)
+ t.end()
+ })
+ })
+ }
+
+ t.test('noent async', function (t) {
+ isexe(noent, options, function (er, is) {
+ t.ok(er)
+ t.notOk(is)
+ t.end()
+ })
+ })
+
+ t.test('noent ignore async', function (t) {
+ isexe(noent, optionsIgnore, function (er, is) {
+ if (er) {
+ throw er
+ }
+ t.notOk(is)
+ t.end()
+ })
+ })
+
+ t.test('directory is not executable', function (t) {
+ isexe(__dirname, options, function (er, is) {
+ if (er) {
+ throw er
+ }
+ t.notOk(is)
+ t.end()
+ })
+ })
+
+ t.end()
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/windows.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/windows.js
new file mode 100644
index 000000000..34996734d
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/isexe/windows.js
@@ -0,0 +1,42 @@
+module.exports = isexe
+isexe.sync = sync
+
+var fs = require('fs')
+
+function checkPathExt (path, options) {
+ var pathext = options.pathExt !== undefined ?
+ options.pathExt : process.env.PATHEXT
+
+ if (!pathext) {
+ return true
+ }
+
+ pathext = pathext.split(';')
+ if (pathext.indexOf('') !== -1) {
+ return true
+ }
+ for (var i = 0; i < pathext.length; i++) {
+ var p = pathext[i].toLowerCase()
+ if (p && path.substr(-p.length).toLowerCase() === p) {
+ return true
+ }
+ }
+ return false
+}
+
+function checkStat (stat, path, options) {
+ if (!stat.isSymbolicLink() && !stat.isFile()) {
+ return false
+ }
+ return checkPathExt(path, options)
+}
+
+function isexe (path, options, cb) {
+ fs.stat(path, function (er, stat) {
+ cb(er, er ? false : checkStat(stat, path, options))
+ })
+}
+
+function sync (path, options) {
+ return checkStat(fs.statSync(path), path, options)
+}
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/CHANGELOG.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/CHANGELOG.md
new file mode 100644
index 000000000..4879fb6fa
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/CHANGELOG.md
@@ -0,0 +1,2380 @@
+# Master (Unreleased)
+
+# 3.1.0 - 8 December, 2023
+
+### Bug fixes
+
+- andWhereNotJsonObject calling wrong function (#5683)
+- PostgreSQL: fix error when setting query_timeout (#5673)
+- MySQL: Missing comments on delete, update and insert (#5738)
+- MySQL: Fixed issue with bigincrements not working with composite primary key - #5341 (#5343)
+
+### Types
+
+- Add type definitions for orHavingNull and orHavingNotNull (#5669)
+- Import knex as type in TS migration template (#5741)
+- Fix conditional constraint error (#5747)
+- PostgreSQL: Fix typing to reflect pg typing change (#5647)
+
+### New features
+
+- Add transactor.parentTransaction (#5567)
+- MySQL: Added implementation for upsert (#5743)
+- Oracle: Support Object Names Greater than 30 Characters for Oracle DB Versions 12.2 and Greater (#5197)
+
+# 3.0.1 - 6 October, 2023
+
+- Build fix
+
+# 3.0.0 - 6 October, 2023
+
+- Fix raw bindings typing (#5401)
+- Fix migrate:unlock when used with custom identifier wrapping. (#5353)
+- Fix driver options specified with .options() method being ignored for oracledb dialect (#5123)
+- Drop compatibility for Node < 16
+- Fix knex d.ts to work with mixed modules (#5659)
+- Fix Lexical error from "Instaed" to "Instead" (#5655)
+
+### Bug fixes
+
+- Fix Linting #5455 - #5460
+
+# 2.5.1 - 12 July, 2023
+
+### Bug fixes
+
+- Fix Linting #5455 - #5460
+
+# 2.5.0 - 08 July, 2023
+
+### New features
+
+- Add uuid helper function (#5617)
+- Add `nativeBindings` option to `better-sqlite3` options (#5461)
+- Add QueryBuilder#updateFrom (#5386)
+- Add readonly transaction access mode (#5445)
+- Add readonly option to Better-SQLite3 (#5530)
+- Add EXCEPT as a valid keyword (#5357)
+- Add ability to prepend query comments (#5289)
+- Add fetchAsString option (#5484)
+
+### Bug fixes
+
+- Avoid password leaks on query logs (#5559)
+- Add knex.mjs to files in package.json (#5518)
+- Handle numeric array elements in .orderBy() (#5551)
+- Attach error handler early enough (#5552)
+- Fix Returning \* in Oracle (#5598)
+- Fix indexType option in `Postgres` (#5601)
+- Add mjs extension type (#5616)
+- Use implicit check on json fields for OracleDB (#5478)
+- Fix when manually close source stream (#5466)
+- Fix case sensitive issue with get table (#5509)
+
+### Typings
+
+- Add Object syntax overload to increment method (#5512)
+- Add object syntax overload to decrement method (#5555)
+- Fix typing for toSql (#5594)
+- Add ResolveTableType for `.merge()` (#5605)
+- Add missing types for havingNull and havingNotNull (#5529)
+- Add collate to the columnbuilder interface (#5568)
+- TableBuilder methods return the SchemaBuilder. (#5486)
+
+# 2.4.2 - 22 January, 2023
+
+### Bug fixes
+
+- CLI: Fix incorrent EOL causing errors on Linux #5455
+
+# 2.4.1 - 18 January, 2023
+
+### Bug fixes
+
+- PostgreSQL: Fix Malformed array literal 2.4.0 Regression #5439
+
+# 2.4.0 - 06 January, 2023
+
+### New features:
+
+- Support partial unique indexes #5316
+- Make compiling SQL in error message optional #5282
+
+### Bug fixes
+
+- Insert array into json column #5321
+- Fix unexpected max acquire-timeout #5377
+- Fix: orWhereJson #5361
+- MySQL: Add assertion for basic where clause not to be object or array #1227
+- SQLite: Fix changing the default value of a boolean column in SQLite #5319
+
+### Typings:
+
+- add missing type for 'expirationChecker' on PgConnectionConfig #5334
+
+# 2.3.0 - 31 August, 2022
+
+### New features:
+
+- PostgreSQL: Explicit jsonb support for custom pg clients #5201
+- SQLite: Support returning with sqlite3 and better-sqlite3 #5285
+- MSSQL: Implement mapBinding mssql dialect option #5292
+
+### Typings:
+
+- Update types for TS 4.8 #5279
+- Fix typo #5267
+- Fix WhereJsonObject withCompositeTableType #5306
+- Fix AnalyticFunction type #5304
+- Infer specific column value type in aggregations #5297
+
+# 2.2.0 - 19 July, 2022
+
+### New features:
+
+- Inline primary key creation for postgres flavours #5233
+- SQLite: Add warning for undefined connection file #5223
+- MSSQL: Add JSON parameter support for connection #5200
+
+### Bug fixes:
+
+- PostgreSQL: add primaryKey option for uuid #5212
+
+### Typings:
+
+- Add promisable and better types #5222
+- Update raw query bind parameter type #5208
+
+# 2.1.0 - 26 May, 2022
+
+### New features:
+
+- Improve bundling experience to safely import dialects while using static paths #5142
+- Implement extendable builders #5041
+- PostgreSQL: Refresh materialized view concurrently #5166
+
+### Bug fixes:
+
+- Use correct paths in package.json browser field #5174
+- MariaDB: Fix 'NULL' returned instead of NULL on MariaDB 10.2.6+ #5181
+- MySQL: fix hasColumn Error (hasColumn ('a_id') is true, but hasColumn('a_Id') is false) #5148
+- MSSQL: Fix .hasTable result when using .withSchema #5176
+- Oracle: correctly INSERTS Buffer #4869
+
+### Typings:
+
+- Update type definitions for pg connection #5139
+
+# 2.0.0 - 21 April, 2022
+
+### Breaking changes
+
+- Restore sqlite3 package #5136
+
+### Test / internal changes:
+
+- Migrate Husky from 4 to 7 #5137
+- Migrate Jake to 10.8.5 #5138
+
+# 1.0.7 - 13 April, 2022
+
+### Bug fixes:
+
+- CLI: Fix cli migrate:make SQLite dependency #5106
+
+# 1.0.6 - 12 April, 2022
+
+### Bug fixes:
+
+- PostgreSQL: Wait for search path to be set before returning connection #5107
+- CLI: No client override during migrate:make #5109
+
+# 1.0.5 - 05 April, 2022
+
+### New features:
+
+- Override knexfile options with CLI options #4047
+
+### Bug fixes:
+
+- Stringify json value in update #5063
+- Fix isModuleType() for yarn #4447
+- Wrapped Unions Fixes #5072
+- SQLite: Fix @vscode-sqlite3 error message #5081
+- CLI: Fix completed migration listing #5060
+
+### Typings:
+
+- Make default generic parameters of `Knex` match the generic parameter types of `knex` #5021
+- Update knex types for TS 4.7 #5095
+
+# 1.0.4 - 13 March, 2022
+
+### New features:
+
+- Add whereLike functions #5044
+
+### Bug fixes:
+
+- Fix orWhereJsonPath clause #5022
+- Subquery in on clause missing parenthesis #5049
+- Rework Union Wrapping #5030
+- Oracle: Fix batch inserts with DEFAULT values with OracleDB #2592 #5037
+
+### Typings:
+
+- Fix types for "returning" methods #5031
+- createTableLike callback should be optional #5055
+
+### Documentation:
+
+- Website URL changed to https://knex.github.io/documentation/
+
+# 1.0.3 - 11 February, 2022
+
+### Bug fixes:
+
+- Fix error message for missing migration files #4937
+- Add withMaterialized and withNotMaterialized to method-constants #5009
+- PostgreSQL: Fix whereJsonPath queries #5011
+- PostgreSQL: Fix delete joins #5016
+- CockroachDB: Fix whereJsonPath queries #5011
+- MySQL: Create primary keys in same statement #5017
+
+### Typings:
+
+- Fix type definition for getMigration in MigrationSource #4998
+- Fix argument type of alter method #4996
+
+### Improvements:
+
+- Use async / await syntax in seeds as default #5005
+
+### Documentation:
+
+- Add Firebird dialect to ECOSYSTEM.md #5003
+
+# 1.0.2 - 02 February, 2022
+
+### New features:
+
+- Support of MATERIALIZED and NOT MATERIALIZED with WITH/CTE #4940
+- Add raw support in onConflict clause #4960
+- Alter nullable constraint when alterNullable is set to true #4730
+- Add alterType parameter for alter function #4967
+- Support string json in json values #4988
+- MySQL: add with clause #4508
+
+### Bug fixes:
+
+- Fix error message for missing migration files #4937
+- Move deferrable to after on update/on delete #4976
+- Do not use sys.tables to find if a table exists #2328
+- PostgreSQL: Fix Order nulls #4989
+- MySQL: Fix collation when renaming column #2666
+- SQLite: Same boolean handling in better-sqlite3 as in sqlite3 #4982
+
+### Typings:
+
+- WhereILike - fix typo #4941
+
+# 1.0.1 - 16 January, 2022
+
+### Bug fixes:
+
+- Fix package.json metadata
+
+# 1.0.0 - 16 January, 2022
+
+### Breaking changes
+
+- Dropped support for Node 10;
+- Replaced unsupported `sqlite3` driver with `@vscode/sqlite3`;
+- Changed data structure from `RETURNING` operation to be consistent with `SELECT`;
+- Changed Migrator to return list of migrations as objects consistently.
+
+### New features:
+
+- Support fromRaw #4781
+- Support zero precision in timestamp/datetime #4784
+- Support whereLike and whereILike #4779
+- Add JSDoc (TS flavor) to stub files #4809
+- Allow skip binding in limit and offset #4811
+- Support creating a new table in the database based on another table #4821
+- Accept Raw on onIn joins #4830
+- Implement support for custom seed sources #4842
+- Add binary uuid option #4836
+- ForUpdate array parameter #4882
+- Add camel case to timestamps method #4803
+- Advanced JSON support #4859
+- Add type to TypeScript knexfile #4909
+- Checks Constraints Support #4874
+- Support creating multiple PKs with increments #4903
+- Enable wrapIdentifier for SQLite .hasTable #4915
+- MSSQL: Add support for unique constraint #4887
+- SQLite: New dialect, using better-sqlite3 driver #4871
+- SQLite: Switch to @vscode/sqlite3 #4866
+- SQLite: Support createViewOrReplace #4856
+- SQLite: Support RETURNING statements for better-sqlite3 driver #4934
+- PostgreSQL: Support JOIN and USING syntax for Delete Statement #4800
+
+### Bug fixes:
+
+- Fix overzealous warning on use of whereNot with "in" or "between" #4780
+- Fix Union all + first syntax error #4799
+- Make view columns optional in create view like #4829
+- Insert lock row fix during migration #4865
+- Fix for createViewOrReplace #4856
+- SQLite: Fix foreign key constraints when altering a table #4189
+- MySQL: Validate connection fix #4794
+- MySQL: Set comment size warning limit to 1024 #4867
+
+### Typings:
+
+- Allow string indexType in index creation #4791
+- Add missing ints typings #4832
+- Returning method types #4881
+- Improve columnInfo type #4868
+
+# 0.95.15 - 22 December, 2021
+
+### Bug fixes:
+
+- Oracle:
+- MariaDB: lock row fix during migration in MariaDB and Oracle #4865
+
+# 0.95.14 - 09 November, 2021
+
+### Bug fixes:
+
+- MySQL: mysql2 dialect validate connection fix #4794
+
+# 0.95.13 - 02 November, 2021
+
+### Bug fixes:
+
+- PostgreSQL: Support zero precision in timestamp/datetime #4784
+
+### Typings:
+
+- Allow string indexType in index creation #4791
+
+# 0.95.12 - 28 October, 2021
+
+### New features:
+
+- New dialect: CockroachDB #4742
+- New dialect: pg-native #4327
+- CockroachDB: add support for upsert #4767
+- PostgreSQL: Support SELECT .. FOR NO KEY UPDATE / KEY SHARE row level locking clauses #4755
+- PostgreSQL: Add support for 'CASCADE' in PostgreSQL 'DROP SCHEMA' queries #4713
+- MySQL: Add storage engine index Type support to index() and unique() schema #4756
+- MSSQL: Support table.primary, table.unique variant with options object #4710
+- SQLite: Add setNullable support to SQLite #4684
+- Add geometry column building #4776
+- Add support for creating table copies #1373
+- Implement support for views and materialized views #1626
+- Implement partial index support #4768
+- Support for 'is null' in 'order by' #3667
+
+### Bug fixes:
+
+- Fix support for Oracle connections passed via knex.connection() #4757
+- Avoid inserting multiple locks if a migration lock already exists #4694
+
+### Typings:
+
+- Some TableBuilder methods return wrong types #4764
+- Update JoinRaw bindings type to accept arrays #4752
+- fix onDelete/onUpdate for ColumnBuilder #4656
+
+# 0.95.11 - 03 September, 2021
+
+### New features:
+
+- Add support for nullability modification via schema builder (table.setNullable() and table.dropNullable()) #4657
+- MySQL: Add support for mysql/mariadb-client JSON parameters in connectionURIs #4629
+- MSSQL: Support comments as MS_Description properties #4632
+
+### Bug fixes:
+
+- Fix Analytic orderBy and partitionBy to follow the SQL documentation #4602
+- CLI: fix migrate:up for migrations disabling transactions #4550
+- SQLite: Fix adding a column with a foreign key constraint in SQLite #4649
+- MSSQL: columnInfo() support case-sensitive database collations #4633
+- MSSQL: Generate valid SQL for withRecursive() #4514
+- Oracle: withRecursive: omit invalid RECURSIVE keyword, include column list #4514
+
+### Improvements:
+
+- Add .mjs migration and seed stubs #4631
+- SQLite: Clean up DDL handling and move all operations to the parser-based approach #4648
+
+# 0.95.10 - 20 August, 2021
+
+### Improvements:
+
+- Use sys info function instead of connection db name #4623
+
+### Typings:
+
+- Deferrable and withkeyName should not be in ColumnBuilder #4600
+
+# 0.95.9 - 31 July, 2021
+
+### New features:
+
+- Oracle: support specifying schema for dropTable and dropSequence #4596
+- Oracle: support specifying schema for autoincrement #4594
+
+### Typings:
+
+- Add TypeScript support for deferrable, new Primary/Unique syntax #4589
+
+# 0.95.8 - 25 July, 2021
+
+### New features:
+
+- Add deferrable support for constraint #4584
+- Implement delete with join #4568
+- Add DPI error codes for Oracle #4536
+
+### Bug fixes:
+
+- Fixing PostgreSQL datetime and timestamp column created with wrong format #4578
+
+### Typings:
+
+- Improve analytic types #4576
+- MSSQL: Add trustServerCertificate option #4500
+
+# 0.95.7 - 10 July, 2021
+
+### New features:
+
+- Add ability to omit columns on an onConflict().ignore() #4557
+- CLI: Log error message #4534
+
+### Typings:
+
+- Export Knex.TransactionConfig #4498
+- Include options object in count(Distinct) typings #4491
+- Add types for analytic functions #4544
+
+# 0.95.6 - 17 May, 2021
+
+### Typings:
+
+- Export TransactionProvider type #4489
+
+# 0.95.5 - 11 May, 2021
+
+### New features:
+
+- SQLite: Add support for file open flags #4446
+- Add .cjs extension to Seeder.js to support Node ESM #4381 #4382
+
+### Bug fixes:
+
+- Remove peerDependencies to avoid auto-install on npm 7 #4480
+
+### Typings:
+
+- Fix typing for increments and bigIncrements #4406
+- Add typings for on JoinClause for onVal #4436
+- Adding Type Definition for isTransaction #4418
+- Export client class from knex namespace #4479
+
+# 0.95.4 - 26 March, 2021
+
+### Typings:
+
+- Fix mistyping of stream #4400
+
+# 0.95.3 - 25 March, 2021
+
+### New features:
+
+- PostgreSQL: Add "same" as operator #4372
+- MSSQL: Improve an estimate of the max comment length #4362
+- Throw an error if negative offset is provided #4361
+
+### Bug fixes:
+
+- Fix timeout method #4324
+- SQLite: prevent dropForeign from being silently ignored #4376
+
+### Typings:
+
+- Allow config.client to be non-client instance #4367
+- Add dropForeign arg type for single column #4363
+- Update typings for TypePreservingAggregation and stream #4377
+
+# 0.95.2 - 11 March, 2021
+
+### New features:
+
+- Improve ESM import support #4350
+
+### Bug fixes:
+
+- CLI: update ts.stub files to new TypeScript namespace #4344
+- CLI: fix TypeScript migration stub after 0.95.0 changes #4366
+
+### Typings:
+
+- Move QueryBuilder and KnexTimeoutError into knex namespace #4358
+
+### Test / internal changes:
+
+- Unify db test helpers #4356
+
+# 0.95.1 - 04 March, 2021
+
+### Bug fixes:
+
+- CLI: fix `knex init` not finding default knexfile #4339
+
+# 0.95.0 - 03 March, 2021
+
+Note: there are many breaking changes in this version, particularly in TypeScript support. Please see `UPGRADING.md` for details.
+
+### New features:
+
+- Add transaction isolation support #4185
+- Add analytic functions #4188
+- Change default to not trigger a promise rejection for transactions with a specified handler #4195
+- Make toSQL().toNative() work for Raw to match the API for QueryBuilder #4058
+- Allow 'match' operator #3569
+- Support optimizer hints #4243
+- Add parameter to prevent autoincrement columns from being primary keys #4266
+- Make "first" and "pluck" mutually exclusive #4280
+- Added merge strategy to allow selecting columns to upsert. #4252
+- Throw error if the array passed to insert is empty #4289
+- Events: introduce queryContext on query-error #4301
+- CLI: Use UTC timestamp for new migrations #4245
+- MSSQL: Replace MSSQL dialect with Tedious.js implementation #2857 #4281
+- MSSQL: Use "nvarchar(max)" for ".json()" #4278
+- MSSQL: Schema builder - add predictable constraint names for default values #4319
+- MSSQL: Schema builder - attempt to drop default constraints when changing default value on columns #4321
+- SQLite: Fallback to json for sqlite3 when using jsonb #4186
+- SQLite: Return complete list of DDL commands for creating foreign keys #4194
+- SQLite: Support dropping composite foreign keys #4202
+- SQLite: Recreate indices when altering a table #4277
+- SQLite: Add support for altering columns #4322
+
+### Bug fixes:
+
+- Fix issue with .withSchema usage with joins on a subquery #4267
+- Fix issue with schema usage with FROM clause contain QueryBuilder, function or Raw #4268
+- CLI: Address raised security warnings by dropping liftoff #4122
+- CLI: Fix an issue with npm@7 and ESM when `type` was set to `'module'` in `package.json` #4295
+- PostgreSQL: Add check to only create native enum once #3658
+- SQLite: Fix foreign key "on delete" when altering a table #4225
+- SQLite: Made the constraint detection case-insensitive #4330
+- MySQL: Keep auto increment after rename #4266
+- MSSQL: don't raise query-error twice #4314
+- MSSQL: Alter column must have its own query #4317
+
+### Typings:
+
+- TypeScript 4.1+ is now required
+- Add missing onConflict overrides #4182
+- Introduce the "infamous triplet" export #4181
+- Fix type definition of Transaction #4172
+- Add typedefinitions for havingNotIn #4265
+- Include 'name' property in MigratorConfig #4300
+- Improve join and conflict types #4318
+- Fix ArrayIfAlready type #4331
+
+### Test / internal changes:
+
+- Drop global Knex.raw #4180
+- Stop using legacy url.parse API #3702
+- Various internal refactorings #4175 #4177 #4178 #4192
+- Refactor to classes #4190 #4191 #4193 #4210 #4253
+- Move transaction type tests to TSD #4208
+- Clean up destroy logic #4248
+- Colorize code snippets in readme files #4234
+- Add "Ecosystem" documentation for Knex plugins #4183
+- Documentation cleanup
+- SQLite: Use SQLite "rename column" instead of a DDL helper #4200
+- SQLite: Simplify reinsert logic when altering a table #4272
+
+# 0.21.19 - 02 March, 2021
+
+- SQLite: Made the constraint detection case-insensitive #4332
+
+# 0.21.18 - 22 February, 2021
+
+- CLI: Fix an issue with npm@7 and ESM when type was set to 'module' in package.json #4295
+
+# 0.21.17 - 30 January, 2021
+
+### Bug fixes:
+
+- SQLite: Fix SQLite foreign on delete when altering a table #4261
+
+### New features:
+
+- Add support for optimizer hints (see https://github.com/knex/documentation/pull/306 for documentation) #4243
+
+# 0.21.16 - 17 January, 2021
+
+### Bug fixes:
+
+- MSSQL: Avoid passing unsupported pool param. Fixes node-mssql 7+ support #4236
+
+# 0.21.15 - 26 December, 2020
+
+### New features:
+
+- SQLite: Add primary/foreign support on alterTable #4162
+- SQLite: Add dropPrimary/dropForeign support on alterTable #4162
+
+### Typings:
+
+- Add "after" and "first" to columnBuilder types #3549 #4169
+
+### Test / internal changes:
+
+- Extract knex config resolution logic #4166
+- Run CI using GitHub Actions #4168
+- Add Node.js 15 to CI matrix #4173
+
+# 0.21.14 - 18 December, 2020
+
+### New features:
+
+- MSSQL: support "returning" on inserts, updates and deletes on tables with triggers #4152
+- Use esm import if package.json type is "module" #4158
+
+### Bug fixes:
+
+- Make sure query-response and query-error events contain \_knexTxId #4160
+
+### Test / internal changes:
+
+- Improved integration test framework #4161
+
+# 0.21.13 - 12 December, 2020
+
+### New features:
+
+- SQLite: Add support for `dropForeign` #4092
+- Add support for WHERE clauses to "upsert" queries #4148
+
+### Bug fixes:
+
+- MSSQL: Avoid connection getting stuck on socket hangup #4157
+- Oracle: Support specifying non-default DB port #4147
+- Oracle: Support inserts with only default values (empty body) #4092
+- CLI: fix irregular seed file execution order #4156
+- Fix performance of asyncStackTraces with enable-source-maps node flag #4154
+
+### Typings:
+
+- PostgreSQL: Add support for application_name #4153
+- Fix types for insert to allow array #4105
+- Add types for userParams and withUserParams #4119
+- Added type for withKeyName #4139
+- Fix batchInsert definitions #4131
+- Fix types for WhereIn signature (value or query builder) #3863
+- Add types for connection config of mysql2 driver #4144
+
+### Test / internal changes:
+
+- Move TS tests to tsd (WIP) #4109 #4110
+
+# 0.21.12 - 02 November, 2020
+
+### Typings:
+
+- Reintroduce support for globally defining table/record mapping #4100
+- Add a few missing types for MSSQL Connection #4103
+- Make .ignore() and .merge() return QueryBuilder rather than QueryInterface #4102
+- Use tarn config TS types instead of generic-pool #4064
+
+# 0.21.11 - 01 November, 2020
+
+### Typings:
+
+- Revert support for globally defining table/record mapping #4099
+
+# 0.21.10 - 31 October, 2020
+
+### New features:
+
+- Upsert support (Postgres/MySQL/Sqlite) #3763
+
+### Bug fixes:
+
+- Switch to non-uuid knexQueryUids to avoid issues when mocking global date #4089
+
+### Typings:
+
+- Allow to globally define table/record mapping #4071
+
+# 0.21.9 - 27 October, 2020
+
+### New features:
+
+- add method clear(statement) to QueryBuilder #4051
+
+### Bug fixes:
+
+- CLI: fix help text being printed twice #4072
+- Oracle: columnInfo() no longer requires an Owner User #4053
+- Add missing "start" event propagation from transaction #4087
+
+# 0.21.8 - 27 October, 2020
+
+### Bug fixes:
+
+- MSSQL: Escape properly if literal '?' is needed #4053
+- Make toQuery behavior consistent with pre-0.21.7 (do not break on empty builder) #4083
+- Fix comment escaping for MySQL and PostgreSQL #4084
+
+# 0.21.7 - 25 October, 2020
+
+### New features:
+
+- CLI: Add migration stub for .cjs extension #4065
+
+### Bug fixes:
+
+- MSSQL: Add dynamic scaling for decimal values and prevents a UInt64 overflow #3910
+- MSSQL: Fix apostrophe escaping #4077
+- Ensure that semicolon is not appended to statements that already end with a semicolon #4052
+
+### Typings:
+
+- Add arguments to QueryCallback in Where #4034
+
+### Test / internal changes:
+
+- Replace lodash type-checks with native solutions #4056
+- Replace mkdirp with native recursive flag #4060
+- Replace inherits package with builtin utility #4059
+
+# 0.21.6 - 27 September, 2020
+
+### New features:
+
+- CLI: New config parameter / CLI flag to prefixing seed filename with timestamp #3873
+- CLI: throw an error when specific seed file cannot be found #4011
+- Warn if whereNot is used with 'in' or 'between' #4038
+
+### Bug fixes:
+
+- CLI: Fix double merging of config for migrator #4040
+
+### Typings:
+
+- Unify SeedsConfig and SeederConfig #4003
+- Allow string[] type for directory in SeedsConfig #4033
+
+# 0.21.5 - 17 August, 2020
+
+### New features:
+
+- CLI: Improve Esm interop #3985
+- CLI: Improve mjs module support #3980
+
+### Test / internal changes:
+
+- Bump version of dtslint #3984
+- Test/document esm interop mixed formats (knexfile/migrations/seeds) #3986
+
+# 0.21.4 - 10 August, 2020
+
+### New features:
+
+- CLI: Add new option for seed: recursive #3974
+
+### Bug fixes:
+
+- CLI: Do not load seeds from subfolders recursively by default #3974
+
+# 0.21.3 - 08 August, 2020
+
+### New features:
+
+- CLI: Support multiple directories for seeds #3967
+
+### Bug fixes:
+
+- Ensure DB stream is destroyed when the PassThrough is destroyed #2324
+- Support postProcessResponse for streams #3931
+- Fix ESM module interop for calling module/package of type 'module' #3938
+- CLI: Fix migration source name in rollback all #3956
+- Fix getMergedConfig calls to include client logger #3920
+- Escape single quoted values passed to defaultTo function #3899
+
+### Typings:
+
+- Add .timeout(ms) to .raw()'s typescript typings #3885
+- Add typing for double table column builder #3950
+- Add a phantom tag to Ref type to mark received type parameters as used #3934
+- Add `null` as valid binding type #3946
+
+### Test / internal changes:
+
+- Change query lab link to https #3933
+
+# 0.21.2 - 10 July, 2020
+
+### New features:
+
+- Warn user if custom migration source is being reset #3839
+- Prefer `void` as return type on migration generator ts stub #3865
+- MSSQL: Added the removal of a columns default constraint, before dropping the column #3855
+
+### Typings:
+
+- Fix definition for raw querybuilders #3846
+
+### Test / internal changes:
+
+- Refactor migration logic to use async/await #3838
+
+# 0.21.1 - 28 April, 2020
+
+### New features:
+
+- CLI: Add migrate:unlock command, truncate on forceFreeMigrationsLock #3822
+- CLI: Add support for cjs files by default #3829
+
+### Bug fixes:
+
+- CLI: Fix inference of seed/migration extension from knexfile extension #3814
+- rewrite delay to not node-only version. Fixes compatibility with browsers #3820
+
+### Test / internal changes:
+
+- Update dependencies. Explicitly support Node.js 14 #3825 #3830
+
+# 0.21.0 - 18 April, 2020
+
+### Improvements
+
+- Reduce size of lodash in bundle #3804
+
+### Breaking changes
+
+- Dropped support for Node 8
+- Breaking upstream change in `pg-query-stream`: `Changed stream.close to stream.destroy which is the official way to terminate a readable stream. This is a breaking change if you rely on the stream.close method on pg-query-stream...though should be just a find/replace type operation to upgrade as the semantics remain very similar (not exactly the same, since internals are rewritten, but more in line with how streams are "supposed" to behave).`
+
+### Test / internal changes:
+
+- Updated Tarn.js to a version 3.0.0
+- Updated mkdirp to a version 1.0.4
+- Updated examples to use ES2015 style #3810
+
+# 0.20.15 - 16 April, 2020
+
+### Bug fixes:
+
+- Support for `.finally(..)` on knex's Promise-alikes #3800
+
+### Typings:
+
+- Add types for `.distinctOn` #3784
+
+# 0.20.14 - 13 April, 2020
+
+### New features:
+
+- CLI: adds support for asynchronous knexfile loading #3748
+- Add clearGroup method #3771
+
+### Typings:
+
+- Support Raw types for insert, where, update #3730
+- Add typings for MigrationSource #3756
+- Update signature of orderBy to support QueryBuilder inside array #3757
+- Add toSQL and toString to SchemaBuilder #3758
+- `interface Knex` and `function Knex` should have the same types #3787
+- Fix minor issues around typings #3765
+
+### Test / internal changes:
+
+- Minor test internal enhancements #3747
+- Minor improvements on the usage of fs utilities #3749
+- Split tests in groups #3785
+
+# 0.20.13 - 23 March, 2020
+
+### Bug fixes:
+
+- Correctly handle dateToString escaping without timezone passed #3742
+- Make protocol length check more defensive #3744
+
+### Typings:
+
+- Make the ChainableInterface conform to Promise #3724
+
+# 0.20.12 - 19 March, 2020
+
+### Bug fixes:
+
+- Added missing call to \_reject in Transactor#transaction #3706
+- Fix method binding on knex proxy #3717
+- Oracle: Transaction_OracleDB can use config.connection #3731
+
+### Typings:
+
+- Fix incorrect type signature of Having #3719
+
+### Test / internal changes:
+
+- Cleanup/remove transaction stalling #3716
+- Rewrote Transaction#acquireConnection() methods to use async #3707
+
+# 0.20.11 - 26 February, 2020
+
+### Breaking changes:
+
+- Knex returns native JS promises instead of Bluebird ones. This means that you no longer use such methods as `map`, `spread` and `reduce` on QueryBuilder instance.
+
+### New features:
+
+- Oracle: Add OracleDB handling for buffer type in fetchAsString #3685
+
+### Bug fixes:
+
+- Fix race condition in non-container transactions #3671
+
+### Typings:
+
+- Mark knex arguments of composite/collection types to be readonly #3680
+
+### Test / internal changes:
+
+- Remove dependency on Bluebird methods from sources #3683
+- Cleanup and extract Transaction Workflow logic #3674
+
+# 0.20.10 - 13 February, 2020
+
+### Bug fixes:
+
+- Oracle: commit was a no-op causing race conditions #3668
+- CLI: Knex calls process.chdir() before opening Knexfile #3661
+- Fixed unresolved promise in cancelQuery() #3666
+
+### Typings:
+
+- `fn.now` takes optionally a precision argument. #3662
+- PG: Include SSL in connection definition #3659
+
+### Test / internal changes:
+
+- replace Bluebird.timeout #3634
+
+# 0.20.9 - 08 February, 2020
+
+### Bug fixes:
+
+- CLI: Improve Support for Liftoff's Preloaders - this should fix some cases like using TS for your migrations #3613
+
+### Typings:
+
+- MSSQL: Add `enableArithAbort` to `MsSqlConnectionConfig`
+
+### Test / internal changes:
+
+- Refactor more tests to use cli-testlab #3640
+- Update QueryCompiler implementation to use classes #3647
+
+# 0.20.8 - 14 January, 2020
+
+### New features:
+
+- CLI: Support ES6 modules via flag --esm #3616
+
+### Bug fixes:
+
+- CLI: Print help only when there are no arguments #3617
+
+### Typings:
+
+- Fix incorrect type of QueryBuilder.first('\*') result #3621
+
+# 0.20.7 - 07 January, 2020
+
+### New features:
+
+- Throw better error when trying to modify schema while using unsupported dialect #3609
+
+### Bug fixes:
+
+- Oracle: dispose connection on connection error #3611
+- Oracle: fix not releasing connection from pool on disconnect #3605
+- CLI: prevent warning with root command #3604
+
+### Typings:
+
+- Add create/drop schema methods to SchemaBuilder #3579
+
+# 0.20.6 - 29 December, 2019
+
+### Bug fixes:
+
+- Enforce Unix (lf) line terminators #3598
+
+# 0.20.5 - 29 December, 2019
+
+### New features:
+
+- Return more information about empty updates #3597
+
+### Bug fixes:
+
+- Fix colors in debug logs #3592
+
+### Test / internal changes:
+
+- Use more efficient algorithm for generating internal ids #3595 #3596
+- Use Buffer.alloc() instead of deprecated constructor #3574
+
+# 0.20.4 - 08 December, 2019
+
+### Bug fixes:
+
+- Fix debug logger messing up queries with % #3566
+- Make logger methods mutually consistent #3567
+
+### Typings:
+
+- Add missing methods to client type #3565
+- Fix queryContext function defintion #3562
+- Fix QueryBuilder.extend this type #3526 #3528
+
+### Test / internal changes:
+
+- Remove bluebird.using #3552
+
+# 0.20.3 - 27 November, 2019
+
+### New features:
+
+- MSSQL, MySQL: Add connection string qs to connection params #3547
+
+### Bug fixes:
+
+- Oracle: Fix issue retrieving BLOB from database #3545
+- PostgreSQL: Timeout for postgresql use cancel instead of terminate #3518
+- Make sure CLI works for namespaced knex packages #2539
+
+### Typings:
+
+- Lift up dialect specific methods in the CreateTableBuilder #3532
+- Add client property to QueryBuilder type #3541
+- Support 'only' option #3551
+
+# 0.20.2 - 14 November, 2019
+
+### New features:
+
+- Add support for distinct on for postgres #3513
+
+### Bug fixes:
+
+- Make sqlite3 hasColumn case insensitive #3435
+
+### Typings:
+
+- Fix PoolConfig typing #3505
+- Expand SeedsConfig types #3531
+- Make the default type parameters of QueryBuilder less strict #3520
+- Fix regression in older version of node when Promise#finally was not available #3507
+
+# 0.20.1 - 29 October, 2019
+
+### New features:
+
+- Declare drivers as optional peerDependencies #3081
+- Dynamic connection configuration resolution #3497
+
+### Bug fixes:
+
+- Wrap subQuery with parenthesis when it appears as table name #3496
+- Fix Oracle error codes #3498
+
+### Typings:
+
+- Add interface for PG Connection object #3372
+- Gracefully handle global promise pollution #3502
+
+# 0.20.0 - 25 October, 2019
+
+### New features:
+
+- orderBy accepts QueryBuilder #3491
+- Add validation in `.offset()` #2908
+- disable_migrations_list_validation feature #3448
+
+### Bug fixes:
+
+- Fix oracledb driver v4 support #3480
+- Fix some issues around seed and migration generation #3479
+- Fix bugs in replacement logic used when dropping columns in SQLite #3476
+
+### Typings:
+
+- Add types to the Migrator interface #3459
+- Fix typings of index and dropIndex TableBuilder methods #3486
+- Fixes types for Seeder#run #3438
+
+### Test / internal changes:
+
+- Execute CI on Node.js 13
+- Bluebird: remove usage of `return`, `reflect`, `fromCallback` methods #3483
+- Bluebird: remove Bluebird.bind #3477
+- Bluebird: use util.promisify instead of Bluebird.promisify #3470
+- Bluebird: remove Bluebird.each #3471
+- Bluebird: remove Bluebird.map and Bluebird.mapSeries #3474
+- Bluebird: replace Bluebird.map with Promise.all #3469
+- Update badges #3482
+
+# 0.19.5 - 06 October, 2019
+
+### New features:
+
+- CLI: Migrations up/down commands - filename parameter #3416
+- Oracle: Support stored procedures #3449
+
+### Bug fixes:
+
+- MSSQL: Escape column ids correctly in all cases (reported by Snyk Security Research Team) #3382
+- SQLite: Fix handling of multiline SQL in SQLite3 schema #3411
+- Fix concurrent child transactions failing #2213 #3440
+
+### Typings:
+
+- Add missing Migrator.list typing #3460
+- Fix Typescript type inference for to better support wildcard (\*) calls #3444
+- Make options argument optional in timeout #3442
+
+### Test / internal changes:
+
+- Enable linting in CI #3450
+
+# 0.19.4 - 09 September, 2019
+
+### New features:
+
+- Add undefined columns to undefined binding(s) error #3425
+
+### Typings:
+
+- Add `specific` to SeederConfig type #3429
+- Fix some issues with QueryBuilder types #3427
+
+# 0.19.3 - 25 August, 2019
+
+### Bug fixes:
+
+- Fix migrations for native enums to use table schema #3307
+
+### New features:
+
+- Add ability to manually define schema for native enums #3307
+- Add SSL/TLS support for Postgres connection string #3410
+- CLI: new command that lists all migrations with status #3390
+
+### Typings:
+
+- Include schemaName in EnumOptions #3415
+- Allow `ColumnBuilder.defaultTo()` to be `null` #3407
+
+### Changes:
+
+- migrate: Refactor \_lockMigrations to avoid forUpdate - makes migrations compatible with CockroachDB #3395
+
+# 0.19.2 - 17 August, 2019
+
+### Changes:
+
+- Make transaction rejection consistent across dialects #3399
+- More consistent handling of nested transactions #3393
+
+### New features:
+
+- Fallback to JSON when using JSONB in MySQL #3394
+
+# 0.19.1 - 23 July, 2019
+
+### New features:
+
+- Allow to extend knex query builder #3334
+- Add .isCompleted() to transaction #3368
+- Minor enhancements around aliasing of aggregates #3354
+
+### Typings:
+
+- Update configuration typings to allow for oracle db connectionstring #3361
+- Update Knex.raw type to be any by default because the actual type is dialect specific #3349
+
+# 0.19.0 - 11 July, 2019
+
+### Changes:
+
+- Pooling: tarn.js connection pool was updated to version 2.0.0. This fixes issue with destroying connections and introduces support for connection pool event handlers. Please see tarn.js documentation for more details #3345
+- Pooling: Passing unsupported pooling configuration options now throws an error
+- Pooling: `beforeDestroy` configuration option was removed
+
+# 0.18.4 - 10 July, 2019
+
+### New features:
+
+- Seeds: Option to run specific seed file #3335
+- Implement "skipLocked()" and "noWait()" #2961
+
+### Bug fixes:
+
+- CLI: Respect the knexfile stub option while generating a migration #3337
+- Fix mssql import not being ignored, breaking webpack builds #3336
+
+# 0.18.3 - 04 July, 2019
+
+### New features:
+
+- CLI: add --stub option to migration:make #3316
+
+### Bug fixes:
+
+- Fix return duplicate transaction promise for standalone transactions #3328
+
+# 0.18.2 - 03 July, 2019
+
+### Bug fixes:
+
+- Fix remove duplicate transaction rejection #3324
+- Fix issues around specifying default values for columns #3318
+- CLI: Fix empty --version output #3312
+
+# 0.18.1 - 30 June, 2019
+
+### Bug fixes:
+
+- Do not reject duplicate promise on transaction rollback #3319
+
+# 0.18.0 - 26 June, 2019
+
+### Bug fixes:
+
+- Do not reject promise on transaction rollback (by default only for new, non-callback, style of transactions for now to avoid breaking old code) #3235
+
+### New features:
+
+- Added `doNotRejectOnRollback` options for starting transactions, to prevent rejecting promises on rollback for callback-style transactions.
+- Use extension from knexfile for generating migrations unless overriden #3282
+- Use migrations.extension from config when generating migration #3242
+- Expose executionPromise for transactors #3297
+
+### Bug fixes:
+
+- Oracle: Updated handling of connection errors for disposal #2608
+- Fix extension resolution from env configs #3294
+
+### Test / internal changes:
+
+- Drop support for Node.js 6 #3227
+- Remove Babel #3227
+- Remove Bluebird #3290 #3287 #3285 #3267 #3266 #3263
+- Fix comments that were modified by find & replace #3308
+
+### Typings:
+
+- Add workarounds for degraded inference when strictNullChecks is set to false #3275
+- Add stub type definition for Migrator config #3279
+- Add stub to seeds type #3296
+- Fix MSSQL config typings #3269
+- Add pgsql specific table builder method typings #3146
+
+# 0.17.5 - 8 June, 2019
+
+### Typings:
+
+- Include result.d.ts in published package #3271
+
+# 0.17.4 - 8 June, 2019
+
+### Typings:
+
+- Fix some cases of left-to-right inference causing type mismatch #3265
+- Improve count typings #3249
+
+### Bug fixes:
+
+- Fix error message bubbling up on seed error #3248
+
+# 0.17.3 - 2 June, 2019
+
+### Typings:
+
+- Improve typings for aggregations #3245
+- Add decimalNumbers to MySqlConnectionConfig interface #3244
+
+# 0.17.2 - 1 June, 2019
+
+### Typings
+
+- Improve count typings #3239
+
+### Bug fixes:
+
+- "colorette" dependency breaks browserify builds #3238
+
+# 0.17.1 - 31 May, 2019
+
+### New features:
+
+- Add migrate:down functionality #3228
+
+### Typings:
+
+- Update type of aggregation results to not be arrays when first has been invoked before #3237
+- Include undefined in type of single row results #3231
+- Fix incorrect type definitions for single row queries #3230
+
+# 0.17.0 - 28 May, 2019
+
+### New features:
+
+- Add support for returning started transaction without immediately executing it #3099
+- Add support for passing transaction around with only starting it when needed #3099
+- Add clearHaving function #3141
+- Add --all flag for rollback in CLI #3187
+- Add error detail log to knex CLI #3149
+- Support multi-column whereIn in sqlite through values clause #3220
+- Allow users to specify the migrations "tableName" parameter via the CLI #3214
+- Unify object options handling for datetime/timestamp across dialects #3181
+- Add "up" command for migrations #3205
+
+### Typings:
+
+- Add default values for generic types (fixes backwards compatibility broken by 0.16.6) #3189
+- Make function types generic in type definitions #3168
+- Add missing types to MigratorConfig #3174
+- Add types for havingBetween, orHavingBetween, havingNotBetween and orHavingNotBetween #3144
+- Update Knex.Config types to include log #3221
+- Fix some more cases of missing typings #3223
+- Support type safe refs #3215
+- Expose some utility types #3211
+- Fix issues with typings of joins and some conflicts with Bluebird typings #3209
+
+### Bug fixes:
+
+- Fix order of migration rollback #3172
+
+### Test / internal changes:
+
+- Execute CI tests on Node.js 12 #3171
+- Docker-based test dbs #3157
+- Use cli-testlab for testing CLI #3191
+
+# 0.16.5 - 11 Apr, 2019
+
+- Bundle polyfills with knex for 0.16.x line again #3139
+
+# 0.16.4 - 11 Apr, 2019
+
+### New features:
+
+- Boolean param for rollback() to rollback all migrations #2968
+- seed:run print the file name of the failing seed #2972 #2973
+- verbose option to CLI commands #2887
+- add intersect() #3023
+- Improved format for TS stubs #3080
+- MySQL: Support nullable timestamps #3100
+- MySQL: Warn `.returning()` does not have any effect #3039
+
+### Bug fixes:
+
+- Respect "loadExtensions" configuration #2969
+- Fix event listener duplication when using Migrator #2982
+- Fix fs-migrations breaking docs #3022
+- Fix sqlite3 drop/renameColumn() breaks with postProcessResponse #3040
+- Fix transaction support for migrations #3084
+- Fix queryContext not being passed to raw queries #3111
+- Typings: Allow to pass query builders, identifiers and raw in various places as parameters #2960
+- Typings: toNative() definition #2996
+- Typings: asCallback() definition #2963
+- Typings: queryContext() type definition Knex.Raw #3002
+- Typings: Add "constraintName" arg to primary() definition #3006
+- Typings: Add missing schemaName in MigratorConfig #3016
+- Typings: Add missing supported parameter types and toSQL method #2960
+- Typings: Update enum arguments to reflect latest signature #3043
+- Typings: Add size parameter to integer method #3074
+- Typings: Add 'string' as accepted Knex constructor type definition #3105
+- Typings: Add boolean as a column name in join #3121
+- Typings: Add missing clearOrder & clearCounters types #3109
+- Dependencies: Fix security warning #3082
+- Do not use unsupported column width/length arguments on data types int and tinyint in MSSQL #2738
+
+### Changes:
+
+- Make unionAll()'s call signature match union() #3055
+
+### Test / internal changes:
+
+- Swap chalk→colorette / minimist→getopts #2718
+- Always use well documented pg client query() config argument #3004
+- Do not bundle polyfills with knex #3024
+
+# 0.16.3 - 19 Dec, 2018
+
+### Bug fixes:
+
+- @babel/polyfill loaded multiple times #2955
+- Resolve migrations and seeds relatively to knexfile directory when specified (the way it used to be before 0.16.1) #2952
+
+# 0.16.2 - 10 Dec, 2018
+
+### Bug fixes:
+
+- Add TypeScript types to the "files" entry so they are properly included in the release #2943
+
+# 0.16.1 - 28 Nov, 2018
+
+### Breaking Changes:
+
+- Use datetime2 for MSSQL datetime + timestamp types. This change is incompatible with MSSQL older than 2008 #2757
+- Knex.VERSION() method was removed, run "require('knex/package').version" instead #2776
+- Knex transpilation now targets Node.js 6, meaning it will no longer run on older Node.js versions #2813
+- Add json type support for SQLite 3.9+ (tested to work with Node package 'sqlite3' 4.0.2+) #2814
+
+### New features:
+
+- Support passing explicit connection to query builder (#2817)
+- Introduced abstraction for getting migrations to make migration bundling easier #2775
+- Allow timestamp with timezone on mssql databases #2724
+- Allow specifying multiple migration directories #2735
+- Allow cloning query builder with .userParams({}) assigned to it #2802
+- Allow chaining of increment, decrement, and update #2740
+- Allow table names with `forUpdate`/`forShare` #2834
+- Added `whereColumn` and the associated `not` / `and` / `or` methods for using columns on the right side of a where clause #2837
+- Added `whereRecursive` method to make self-referential CTEs possible #2889
+- Added support for named unique, primary and foreign keys to SQLite3 #2840
+- Added support for generating new migration and seed files without knexfile #2884 #2905 #2935
+- Added support for multiple columns in `.orderBy()` #2881
+- Added option of `existingType` to `.enum()` method to support repeated use of enums #2719
+- Added option to pass `indexType` for MySQL dialect #2890
+- Added `onVal` and the associated `not` / `and` / `or` methods for using values in `on` clauses within joins #2746
+- Kill queries after timeout for PostgreSQL #2636
+- Manage TypeScript types internally #2845
+- Support 5.0.0+ versions of mssql driver #2861
+- Typescript migration stub #2816
+- Options object for passing timestamp parameters + regression tests #2919
+
+### Bug fixes:
+
+- Implement fail-fast logic for dialect resolution #2776
+- Fixed identifier wrapping for `using()`. Use columnize instead of wrap in using() #2713
+- Fix issues with warnPromise when migration does not return a promise #2730
+- Compile with before update so that bindings are put in correct order #2733
+- Fix join using builder withSchema #2744
+- Throw instead of process.exit when client module missing #2843
+- Display correct filename of a migration that failed #2910
+- Fixed support of knexSnakeCaseWrappers in migrations #2914
+- SQlite3 renameColunm quote fix #2833
+- Adjust typing for forUpdate()/forShare() variant with table names #2858
+- Fix execution of Oracle tests on Node 11 #2920
+- Fix failures in oracle test bench and added it back to mandatory CI tests #2924
+- Knex client knexfile resolution fix #2923
+- Add queryContext to type declarations #2931
+
+### Test / internal changes:
+
+- Add tests for multiple union arguments with callbacks and builders #2749
+- Update dependencies #2772 #2810 #2842 #2848 #2893 #2904
+- Separate migration generator #2786
+- Do not postprocess internal queries in Migrator #2914 #2934
+- Use Babel 7 #2813
+- Introduce LGTM.com badge #2755
+- Cleanup based on analysis by https://lgtm.com #2870
+- Add test for retrieving null dates #2865
+- Add link to wiki #2866
+- Add tests for specifying explicit pg version #2895
+- Execute tests on Node.js 11 #2873
+- Version upgrade guide #2894
+
+# 0.16.0 - 27 Nov, 2018
+
+### Changes:
+
+- THIS RELEASE WAS UNPUBLISHED FROM NPM BECAUSE IT HAD BROKEN MIGRATIONS USING `postprocessResponse` FEATURE (#2644)
+
+# 0.15.2 - 19 Jul, 2018
+
+### Changes:
+
+- Rolled back changes introduced by #2542, in favor of opt-in behavior by adding a precision option in `date` / `timestamp` / `datetime` / `knex.fn.now` (#2715, #2721)
+
+# 0.15.1 - 12 Jul, 2018
+
+### Bug fixes:
+
+- Fix warning erroneously displayed for mysql #2705
+
+# 0.15.0 - 1 Jul, 2018
+
+### Breaking Changes:
+
+- Stop executing tests on Node 4 and 5. #2451 (not supported anymore)
+- `json` data type is no longer converted to `text` within a schema builder migration for MySQL databases (note that JSON data type is only supported for MySQL 5.7.8+) #2635
+- Removed WebSQL dialect #2461
+- Drop mariadb support #2681
+- Primary Key for Migration Lock Table #2569. This shouldn't affect to old loc tables, but if you like to have your locktable to have primary key, delete the old table and it will be recreated when migrations are ran next time.
+- Ensure knex.destroy() returns a bluebird promise #2589
+- Increment floats #2614
+- Testing removal of 'skim' #2520, Now rows are not converted to plain js objects, returned row objects might have changed type with oracle, mssql, mysql and sqlite3
+- Drop support for strong-oracle #2487
+- Timeout errors doesn't silently ignore the passed errors anymore #2626
+- Removed WebSQL dialect #2647
+- Various fixes to mssql dialect to make it compatible with other dialects #2653, Unique constraint now allow multiple null values, float type is now float instead of decimal, rolling back transaction with undefined rejects with Error, select for update and select for share actually locks selected row, so basically old schema migrations will work a lot different and produce different schema like before. Also now MSSQL is included in CI tests.
+
+### Bug fixes:
+
+- Fixes onIn with empty values array #2513
+- fix wrapIdentifier not being called in postgres alter column #2612
+- fixes wrapIdentifier to work with postgres `returning` statement 2630 #2642
+- Fix mssql driver crashing in certain cases when conneciton is closed unexpectedly #2637
+- Removed semicolon from rollback stmt for oracle #2564
+- Make the stream catch errors in the query #2638
+
+### New Features:
+
+- Create timestamp columns with microsecond precision on MySQL 5.6 and newer #2542
+- Allow storing stacktrace, where builder is initialized to be able trace back where certain query was created #2500 #2505
+- Added 'ref' function #2509, no need for knex.raw('??', ['id']) anymore, one can do knex.ref('id')
+- Support postgresql connection uri protocol #2609
+- Add support for native enums on Postgres #2632
+- Allow overwriting log functions #2625
+
+### Test / internal changes:
+
+- chore: cache node_modules #2595
+- Remove babel-plugin-lodash #2634
+- Remove readable-stream and safe-buffer #2640
+- chore: add Node.js 10 #2594
+- add homepage field to package.json #2650
+
+# 0.14.6 - 12 Apr, 2018
+
+### Bug fixes:
+
+- Restored functionality of query event #2566 (#2549)
+
+# 0.14.5 - 8 Apr, 2018
+
+### Bug fixes:
+
+- Fix wrapping returning column on oracledb #2554
+
+### New Features:
+
+- Support passing DB schema name for migrations #2499 #2559
+- add clearOrder method #2360 #2553
+- Added knexTxId to query events and debug calls #2476
+- Support multi-column `whereIn` with query #1390
+- Added error if chaining update/insert/etc with first() #2506
+- Checks for an empty, undefined or null object on transacting #2494
+- countDistinct with multiple columns #2449
+
+### Test / internal changes:
+
+- Added npm run test:oracledb command that runs oracledb tests in docker #2491
+- Runnin mssql tests in docker #2496
+- Update dependencies #2561
+
+# 0.14.4 - 19 Feb, 2018
+
+### Bug fixes:
+
+- containsUndefined only validate plain objects. Fixes #1898 (#2468)
+- Add warning when using .returning() in sqlite3. Fixes #1660 (#2471)
+- Throw an error if .update() results in an empty sql (#2472)
+- Removed unnecessary createTableIfNotExist and replaced with createTable (#2473)
+
+### New Features:
+
+- Allow calling lock procedures (such as forUpdate) outside of transaction. Fixes #2403. (#2475)
+- Added test and documentation for Event 'start' (#2488)
+
+### Test / internal changes:
+
+- Added stress test, which uses TCP proxy to simulate flaky connection #2460
+- Removed old docker tests, new stress test setup (#2474)
+- Removed unused property \_\_cid on the base client (#2481)
+- Changed rm to rimraf in 'npm run dev' (#2483)
+- Changed babel preset and use latest node as target when running dev (#2484)
+
+# 0.14.3 - 8 Feb, 2018
+
+### Bug fixes:
+
+- Use tarn as pool instead of generic-pool which has been given various problems #2450
+- Fixed mysql issue where add columns failed if using both after and collate #2432
+- CLI sets exit-code 1 if the command supplied was not parseable #2358
+- Set toNative() to be not enumerable #2388
+- Use wrapIdentifier in columnInfo. fixes #2402 #2405
+- Fixed a bug when using .returning (OUTPUT) in an update query with joins in MSSQL #2399
+- Better error message when running migrations fail before even starting run migrations #2373
+- Read oracle's UV_THREADPOOL_SIZE env variable correctly #2372
+- Added decimal variable precision / scale support #2353
+
+### New Features:
+
+- Added queryContext to schema and query builders #2314
+- Added redshift dialect #2233
+- Added warning when one uses .createTableIfNotExist and deprecated it from docs #2458
+
+### Test / internal changes:
+
+- Update dependencies and fix ESLint warnings accordingly #2433
+- Disable oracledb tests from non LTS nodes #2407
+- Update dependencies #2422
+
+# 0.14.2 - 24 Nov, 2017
+
+### Bug fixes:
+
+- Fix sqlite3 truncate method to work again #2348
+
+# 0.14.1 - 19 Nov, 2017
+
+### Bug fixes:
+
+- Fix support for multiple schema names in in postgres `searchPath` #2340
+- Fix create new connection to pass errors to query instead of retry loop #2336
+- Fix recognition of connections closed by server #2341
+
+# 0.14.0 - 6 Nov, 2017
+
+### Breaking Changes:
+
+- Remove sorting of statements from update queries #2171
+- Updated allowed operator list with some missing operators and make all to lower case #2239
+- Use node-mssql 4.0.0 #2029
+- Support for enum columns to SQlite3 dialect #2055
+- Better identifier quoting in Sqlite3 #2087
+- Migration Errors - Display filename of of failed migration #2272
+
+### Other Features:
+
+- Post processing hook for query result #2261
+- Build native SQL where binding parameters are dialect specific #2237
+- Configuration option to allow override identifier wrapping #2217
+- Implemented select syntax: select({ alias: 'column' }) #2227
+- Allows to filter seeds and migrations by extensions #2168
+- Reconnecting after database server disconnect/reconnect + tests #2017
+- Removed filering from allowed configuration settings of mysql2 #2040
+- Allow raw expressions in query builder aggregate methods #2257
+- Throw error on non-string table comment #2126
+- Support for mysql stream query options #2301
+
+### Bug fixes:
+
+- Allow update queries and passing query builder to with statements #2298
+- Fix escape table name in SQLite columnInfo call #2281
+- Preventing containsUndefined from going to recursion loop #1711
+- Fix error caused by call to knex.migrate.currentVersion #2123
+- Upgraded generic-pool to 3.1.7 (did resolve some memory issues) #2208
+- Allow using NOT ILIKE operator #2195
+- Fix postgres searchPath to be case-sensitive #2172
+- Fix drop of multiple columns in sqlite3 #2107
+- Fix adding multiple columns in Oracle #2115
+- Use selected schema when dropping indices in Postgres. #2105
+- Fix hasTable for MySQL to not do partial matches #2097
+- Fix setting autoTransaction in batchInsert #2113
+- Fix connection error propagation when streaming #2199
+- Fix comments not being applied to increments columns #2243
+- Fix mssql wrong binding order of queries that combine a limit with select raw or update #2066
+- Fixed mysql alter table attributes order #2062
+
+### Test / internal changes:
+
+- Update each out-of-date dependency according to david-dm.org #2297
+- Update v8flags to version 3.0.0 #2288
+- Update interpret version #2283
+- Fix debug output typo #2187
+- Docker CI tests #2164
+- Unit test for right/rightOuterJoin combination #2117
+- Unit test for fullOuterJoin #2118
+- Unit tests for table comment #2098
+- Test referencing non-existent column with sqlite3 #2104
+- Unit test for renaming column in postgresql #2099
+- Unit test for cross-join #2102
+- Fix incorrect parameter name #2068
+
+# 0.13.0 - 29 Apr, 2017
+
+### Breaking Changes:
+
+- Multiple concurrent migration runners blocks instead of throwing error when possible #1962
+- Fixed transaction promise mutation issue #1991
+
+### Other Changes:
+
+- Allow passing version of connected db in configuration file #1993
+- Bugfixes on batchInsert and transactions for mysql/maria #1992
+- Add fetchAsString optional parameter to oracledb dialect #1998
+- fix: escapeObject parameter order for Postgres dialect. #2003
+
+# 0.12.9 - 23 Mar, 2017
+
+- Fixed unhandled exception in batchInsert when the rows to be inserted resulted in duplicate key violation #1880
+
+# 0.12.8 - 15 Mar, 2017
+
+- Added clearSelect and clearWhere to query builder #1912
+- Properly close Postgres query streams on error #1935
+- Transactions should never reject with undefined #1970
+- Clear acquireConnectionTimeout if an error occurs when acquiring a connection #1973
+
+# 0.12.7 - 17 Feb, 2017
+
+### Accidental Breaking Change:
+
+- Ensure that 'client' is provided in knex config object #1822
+
+### Other Changes:
+
+- Support custom foreign key names #1311, #1726
+- Fixed named bindings to work with queries containing `:`-chars #1890
+- Exposed more promise functions #1896
+- Pass rollback errors to transaction promise in mssql #1885
+- ONLY keyword support for PostgreSQL (for table inheritance) #1874
+- Fixed Mssql update with join syntax #1777
+- Replace migrations and seed for react-native packager #1813
+- Support knexfile, migration and seeds in TypeScript #1769
+- Fix float to integer conversion of decimal fields in MSSQL #1781
+- External authentication capability when using oracledb driver #1716
+- Fixed MSSQL incorect query build when locks are used #1707
+- Allow to use `first` method as aliased select #1784
+- Alter column for nullability, type and default value #46, #1759
+- Add more having* methods / join clause on* methods #1674
+- Compatibility fixes and cleanups #1788, #1792, #1794, #1814, #1857, #1649
+
+# 0.12.6 - 19 Oct, 2016
+
+- Address warnings mentioned in #1388 (#1740)
+- Remove postinstall script (#1746)
+
+# 0.12.5 - 12 Oct, 2016
+
+- Fix broken 0.12.4 build (removed from npm)
+- Fix #1733, #920, incorrect postgres array bindings
+
+# 0.12.3 - 9 Oct, 2016
+
+- Fix #1703, #1694 - connections should be returned to pool if acquireConnectionTimeout is triggered
+- Fix #1710 regression in postgres array escaping
+
+# 0.12.2 - 27 Sep, 2016
+
+- Restore pool min: 1 for sqlite3, #1701
+- Fix for connection error after it's closed / released, #1691
+- Fix oracle prefetchRowCount setting, #1675
+
+# 0.12.1 - 16 Sep, 2016
+
+- Fix MSSQL sql execution error, #1669
+- Added DEBUG=knex:bindings for debugging query bindings, #1557
+
+# 0.12.0 - 13 Sep, 2016
+
+- Remove build / built files, #1616
+- Upgrade to Babel 6, #1617
+- Reference Bluebird module directly, remove deprecated .exec method, #1618
+- Remove documentation files from main repo
+- Fix broken behavior on WebSQL build, #1638
+- Oracle id sequence now handles manual inserts, #906
+- Cleanup PG escaping, fix #1602, #1548
+- Added [`with`](#Builder-with) to builder for [common table expressions](https://www.postgresql.org/docs/9.4/static/queries-with.html), #1599
+- Fix #1619, pluck with explicit column names
+- Switching back to [generic-pool](https://github.com/coopernurse/node-pool) for pooling resource management
+- Removed index.html, please direct all PR's for docs against the files in [knex/documentation](https://github.com/knex/documentation)
+
+# 0.11.10 - 9 Aug, 2016
+
+- Added CHANGELOG.md for a [new documentation](https://github.com/knex/documentation) builder coming soon, #1615
+- Minor documentation tweaks
+- PG: Fix Uint8Array being considered undefined, #1601
+- MSSQL: Make columnInfo schema dynamic, #1585
+
+# 0.11.9 - 21 Jul, 2016
+
+- Reverted knex client breaking change (commit b74cd69e906), fixes #1587
+
+# 0.11.8 - 21 Jul, 2016
+
+- Oracledb dialect #990
+- Documentation fix #1532
+- Allow named bindings to be escaped. #1576
+- Several bugs with MS SQL schema creation and installing from gihub fix #1577
+- Fix incorrect escaping of backslashes in SqlString.escape #1545
+
+# 0.11.7 - 19 Jun, 2016
+
+- Add missing dependency. #1516
+
+# 0.11.6 - 18 Jun, 2016
+
+- Allow cancellation on timeout (MySQL) #1454
+- Better bigint support. (MSSQL) #1445
+- More consistent handling of `undefined` values in `QueryBuilder#where` and `Raw`. #1459
+- Fix Webpack build. #1447
+- Fix code that triggered Bluebird warnings. #1460, #1489
+- Fix `ping` function. (Oracle) #1486
+- Fix `columnInfo`. (MSSQL) #1464
+- Fix `ColumnCompiler#binary`. (MSSQL) #1464
+- Allow connection strings that do not contain a password. #1473
+- Fix race condition in seed stubs. #1493
+- Give each query a UUID. #1510
+
+# 0.11.5 - 26 May, 2016
+
+- Bugfix: Using `Raw` or `QueryBuilder` as a binding to `Raw` now works as intended
+
+# 0.11.4 - 22 May, 2016
+
+- Bugfix: Inconsistency of `.primary()` and `.dropPrimary()` between dialects #1430
+- Feature: Allow using custom Client/Dialect (you can pass your own client in knex config) #1428
+- Docs: Add documentation for .dropTimestamps #1432
+- Bugfix: Fixed passing undefined fields for insert/update inside transaction #1423
+- Feature: `batchInsert` with existing transaction #1354
+- Build: eslint instead of jshint #1416
+- Bugfix: Pooled connections not releasing #1382
+- Bugfix: Support passing `knex.raw` to `.whereNot` #1402
+- Docs: Fixed list of dialects which supports `.returning` #1398
+- Bugfix: rename table does not fail anymore even with schema defined #1403
+
+# 0.11.3 - 14 May, 2016
+
+- Support nested joins. #1397
+
+# 0.11.2 - 14 May, 2016
+
+- Prevent crash on `knex seed:make`. #1389
+- Improvements to `batchInsert`. #1391
+- Improvements to inserting `DEFAULT` with `undefined` binding. #1396
+- Correct generated code for adding/dropping multiple columns. (MSSQL) #1401
+
+# 0.11.1 - 6 May, 2016
+
+- Fix error in CLI command `migrate:make`. #1386
+
+# 0.11.0 - 5 May, 2016
+
+### Breaking Changes:
+
+- `QueryBuilder#orWhere` joins multiple arguments with `AND`. #1164
+
+### Other Changes:
+
+- Collate for columns. (MySQL) #1147
+- Add `QueryBuilder#timeout`, `Raw#timeout`. #1201 #1260
+- Exit with error code when appropriate. #1238
+- MSSQL connection accepts `host` as an alias for `server` in accordance with other dialects. #1239
+- Add `query-response` event. #1231
+- Correct behaviour of sibling nested transactions. #1226
+- Support `RETURNING` with `UPDATE`. (Oracle) #1253
+- Throwing callbacks from transactions automatically rolls them back. #1257
+- Fixes to named `Raw` bindings. #1251
+- `timestamps` accepts an argument to set `NOT NULL` and default to current timestamp.
+- Add `TableBuilder#inherits` for PostgreSQL. #601
+- Wrap index names. #1289
+- Restore coffeescript knexfiles and configurations. #1292
+- Add `andWhereBetween` and `andWhereNotBetween` #1132
+- Fix `valueForUndefined` failure. #1269
+- `renameColumn` no longer drops default value or nullability. #1326
+- Correct MySQL2 error handling. #1315
+- Fix MSSQL `createTableIfNotExists`. #1362
+- Fix MSSQL URL parsing. #1342
+- Update Lodash to 4.6.0 #1242
+- Update Bluebird to 3.3.4 #1279
+
+# 0.10.0 - 15 Feb, 2016
+
+### Breaking Changes:
+
+- `insert` and `update` now ignore `undefined` values. Back compatibility is provided through the option `useNullAsDefault`. #1174, #1043
+
+### Other Changes:
+
+- Add [`countDistinct`](#Builder-countDistinct), [`avgDistinct`](#Builder-avgDistinct) and [`sumDistinct`](#Builder-sumDistinct). #1046
+- Add [`schema.jsonb`](#Schema-jsonb). Deprecated `schema.json(column, true)`. #991
+- Support binding identifiers with `??`. #1103
+- Restore `query` event when triggered by transactions. #855
+- Correct question mark escaping in rendered queries. #519, #1058
+- Add per-dialect escaping, allowing quotes to be escaped correctly. #886, #1095
+- Add MSSQL support. #1090
+- Add migration locking. #1094
+- Allow column aliases to contain `.`. #1181
+- Add `batchInsert`. #1182
+- Support non-array arguments to [`knex.raw`](#Raw-Bindings).
+- Global `query-error` event. #1163
+- Add `batchInsert`. #1182
+- Better support for Mysql2 dialect options. #980
+- Support for `acquireConnectionTimeout` default 60 seconds preventing #1040 from happening. #1177
+- Fixed constraint name escaping when dropping a constraint. #1177
+- Show also `.raw` queries in debug output. #1169
+- Support for `cli` to use basic configuration without specific environment set. #1101
+
+# 0.9.0 - Nov 2, 2015
+
+- Fix error when merging `knex.raw` instances without arguments. #853
+- Fix error that caused the connection to time out while streaming. #849
+- Correctly parse SSL query parameter for PostgreSQL. #852
+- Pass `compress` option to MySQL2. #843
+- Schema: Use `timestamp with timezone` by default for `time`, `datetime` and `timestamp` for Oracle. #876
+- Add [`QueryBuilder#modify`](#Builder-modify) #881
+- Add LiveScript and Early Gray support for seeds and migrations.
+- Add [`QueryBuilder#withSchema`](#Builder-withSchema) #518
+- Allow escaping of `?` in `knex.raw` queries. #946
+- Allow `0` in join clause. #953
+- Add migration config to allow disabling/enabling transactions per migration. #834
+
+# 0.8.6 - May 20, 2015
+
+- Fix for several transaction / migration issues, #832, #833, #834, #835
+
+# 0.8.5 - May 14, 2015
+
+- Pool should be initialized if no pool options are specified
+
+# 0.8.4 - May 13, 2015
+
+- Pool should not be initialized if {max: 0} is sent in config options
+
+# 0.8.3 - May 2, 2015
+
+- Alias postgresql -> postgres in connection config options
+
+# 0.8.2 - May 1, 2015
+
+- Fix regression in using query string in connection config
+
+# 0.8.1 - May 1, 2015
+
+- Warn rather than error when implicit commits wipe out savepoints in mysql / mariadb, #805.
+- Fix for incorrect seed config reference, #804
+
+# 0.8.0 - Apr 30, 2015
+
+### New Features:
+
+- Fixes several major outstanding bugs with the connection pool, switching to [Pool2](https://github.com/myndzi/pool2) in place of generic-pool-redux
+- strong-oracle module support
+- Nested transactions automatically become savepoints, with `commit` & `rollback` releasing or rolling back the current savepoint.
+- Database seed file support, #391
+- Improved support for sub-raw queries within raw statements
+- Migrations are now wrapped in transactions where possible
+- Subqueries supported in insert statements, #627
+- Support for nested having, #572
+- Support object syntax for joins, similar to "where" #743
+
+### Major Changes:
+
+- Transactions are immediately invoked as A+ promises, #470 (this is a feature and should not actually break anything in practice)
+- Heavy refactoring internal APIs (public APIs should not be affected)
+
+### "Other Changes:
+
+- Allow mysql2 to use non-default port, #588
+- Support creating & dropping extensions in PostgreSQL, #540
+- CLI support for knexfiles that do not provide environment keys, #527
+- Added sqlite3 dialect version of whereRaw/andWhereRaw (#477)
+
+# 0.7.5 - Mar 9, 2015
+
+- Fix bug in validateMigrationList, (#697)
+
+# 0.7.4 - Feb 25, 2015
+
+- Fix incorrect order of query parameters when using subqueries, #704
+- Properly handle limit 0, (#655)
+- Apply promise args from then instead of [explicitly passing](https://github.com/petkaantonov/bluebird/issues/482).
+- Respect union parameter as last argument (#660).
+- Added sqlite3 dialect version of whereRaw/andWhereRaw (#477).
+- Fix SQLite dropColumn doesn't work for last column (#544).
+- Add POSIX operator support for Postgres (#562)
+- Sample seed files now correctly (#391)
+
+# 0.7.3 - Oct 3, 2014
+
+- Support for `join(table, rawOrBuilder)` syntax.
+- Fix for regression in PostgreSQL connection (#516)
+
+# 0.7.2 - Oct 1, 2014
+
+- Fix for regression in migrations
+
+# 0.7.1 - Oct 1, 2014
+
+- Better disconnect handling & pool removal for MySQL clients, #452
+
+# 0.7.0 - Oct 1, 2014
+
+### New Features:
+
+- Oracle support, #419
+- Database seed file support, #391
+- Improved support for sub-raw queries within raw statements
+
+### Breaking Changes:
+
+- "collate nocase" no longer used by default in sqlite3 #396
+
+### Other Changes:
+
+- Bumping Bluebird to ^2.x
+- Transactions in websql are now a no-op (unsupported) #375
+- Improved test suite
+- knex.fn namespace as function helper (knex.fn.now), #372
+- Better handling of disconnect errors
+- Support for offset without limit, #446
+- Chainable first method for mysql schema, #406
+- Support for empty array in `whereIn`
+- Create/drop schema for postgres, #511
+- Inserting multiple rows with default values, #468
+- Join columns are optional for cross-join, #508
+- Flag for creating jsonb columns in Postgresql, #500
+
+# 0.6.22 - July 10, 2014
+
+- Bug fix for properly binding postgresql streaming queries, (#363)
+
+# 0.6.21 - July 9, 2014
+
+- Bug fix for raw queries not being transaction context aware, (#351).
+- Properly forward stream errors in sqlite3 runner, (#359)
+
+# 0.6.20 - June 30, 2014
+
+- Allow case insensitive operators in sql clauses, (#344)
+
+# 0.6.19 - June 27, 2014
+
+- Add `groupByRaw` / `orderByRaw` methods, better support for raw statements in group / order (#282).
+- Support more config options for node-mysql2 dialect (#341).
+- CLI help text fix, (#342)
+
+# 0.6.18 - June 25, 2014
+
+- Patch for the method, calling without a handler should return the stream, not a promise (#337)
+
+# 0.6.17 - June 23, 2014
+
+- Adding missing map / reduce proxies to bluebird's implementation
+
+# 0.6.16 - June 18, 2014
+
+- Increment / decrement returns the number of affectedRows (#330).
+- Allow --cwd option flag to be passed to CLI tool (#326)
+
+# 0.6.15 - June 14, 2014
+
+- Added the as method for aliasing subqueries
+
+# 0.6.14 - June 14, 2014
+
+- whereExists / whereNotExists may now take a query builder instance as well as a callback
+
+# 0.6.13 - June 12, 2014
+
+- Fix regression with onUpdate / onDelete in PostgreSQL, (#308).
+- Add missing `Promise` require to knex.js, unit test for knex.destroy (#314)
+
+# 0.6.12 - June 10, 2014
+
+- Fix for regression with boolean default types in PostgreSQL
+
+# 0.6.11 - June 10, 2014
+
+- Fix for regression with queries containing multiple order by statements in sqlite3
+
+# 0.6.10 - June 10, 2014
+
+- Fix for big regression in memoization of column names from 0.5 -> 0.6
+
+# 0.6.9 - June 9, 2014
+
+- Fix for regression in specificType method
+
+# 0.6.8 - June 9, 2014
+
+- Package.json fix for CLI
+
+# 0.6.7 - June 9, 2014
+
+- Adds support for [node-mysql2](https://github.com/sidorares/node-mysql2) library.
+- Bundles CLI with the knex install, various related migrate CLI fixes
+
+# 0.6.6 - June 9, 2014
+
+- console.warn rather than throw when adding foreignKeys in SQLite3.
+- Add support for dropColumn in SQLite3.
+- Document `raw.wrap`
+
+# 0.6.5 - June 9, 2014
+
+- Add missing \_ require to WebSQL builds
+
+# 0.6.4 - June 9, 2014
+
+- Fix & document schema.raw method
+
+# 0.6.3 - June 6, 2014
+
+- Schema methods on transaction object are now transaction aware (#301).
+- Fix for resolved value from transactions, (#298).
+- Undefined columns are not added to builder
+
+# 0.6.2 - June 4, 2014
+
+- Fix regression in raw query output, (#297).
+- Fix regression in "pluck" method (#296).
+- Document [first](#Builder-first) method
+
+# 0.6.1 - June 4, 2014
+
+- Reverting to using .npmignore, the "files" syntax forgot the knex.js file
+
+# 0.6.0 - June 4, 2014
+
+### Major Library refactor:
+
+- Major internal overhaul to clean up the various dialect code.
+- Improved unit test suite.
+- Support for the [mariasql](https://github.com/mscdex/node-mariasql) driver.
+- More consistent use of raw query bindings throughout the library.
+- Queries are more composable, may be injected in various points throughout the builder.
+- Added [streaming](#Interfaces-Streams) interface
+- Deprecated 5 argument [join](#Builder-join) in favor of additional join methods.
+- The wrapValue function to allow for array column operations in PostgreSQL (#287).
+- An explicit connection can be passed for any query (#56).
+- Drop column support for sqlite3
+- All schema actions are run sequentially on the same connection if chained.
+- Schema actions can now be wrapped in a transaction
+- `.references(tableName.columnName)` as shorthand for `.references(columnName).inTable(tableName)`
+- `.join('table.column', 'otherTable.column')` as shorthand for .join('table.column', '=', 'otherTable.column')
+- Streams are supported for selects, passing through to the streaming capabilities of node-mysql and node-postgres
+- For More information, see this [pull-request](https://github.com/tgriesser/knex/pull/252)
+
+# 0.5.15 - June 4, 2014
+
+- Dropped indexes feature now functions correctly, (#278)
+
+# 0.5.14 - May 6, 2014
+
+- Remove the charset encoding if it's utf8 for mysql, as it's the default but also currently causes some issues in recent versions of node-mysql
+
+# 0.5.13 - April 2, 2014
+
+- Fix regression in array bindings for postgresql (#228)
+
+# 0.5.12 - Mar 31, 2014
+
+- Add more operators for where clauses, including && (#226)
+
+# 0.5.11 - Mar 25, 2014
+
+- `.where(col, 'is', null)` or `.where(col, 'is not', null)` are not supported (#221).
+- Case insensitive `where` operators now allowed (#212).
+- Fix bug in increment/decrement truncating to an integer (#210).
+- Disconnected connections are now properly handled & removed from the pool (#206).
+- Internal tweaks to binding concatenations for performance (#207)
+
+# 0.5.10 - Mar 19, 2014
+
+- Add the .exec method to the internal promise shim
+
+# 0.5.9 - Mar 18, 2014
+
+- Remove error'ed connections from the connection pool (#206), added support for node-postgres-pure (pg.js) (#200)
+
+# 0.5.8 - Feb 27, 2014
+
+- Fix for chaining on forUpdate / forShare, adding map & reduce from bluebird
+
+# 0.5.7 - Feb 18, 2014
+
+- Fix for a null limit / offset breaking query chain (#182)
+
+# 0.5.6 - Feb 5, 2014
+
+- Bump bluebird dependency to ~1.0.0, fixing regression in Bluebird 1.0.2 (#176)
+
+# 0.5.5 - Jan 28, 2014
+
+- Fix for the exit code on the migrations cli (#151).
+- The `init` method in `knex.migrate` now uses `this.config` if one isn't passed in (#156)
+
+# 0.5.4 - Jan 7, 2014
+
+- Fix for using raw statements in defaultTo schema builder methods (#146)
+
+# 0.5.3 - Jan 2, 2014
+
+- Fix for incorrectly formed sql when aggregates are used with columns (#144)
+
+# 0.5.2 - Dec 18, 2013
+
+- Adding passthrough "catch", "finally" to bluebird implementations, use bluebird's "nodeify" internally for exec
+
+# 0.5.1 - Dec 12, 2013
+
+- The [returning](#Builder-returning) in PostgreSQL may now accept \* or an array of columns to return. If either of these are passed, the response will be an array of objects rather than an array of values. Updates may also now use a `returning` value. (#132)
+- Added `bigint` and `bigserial` type to PostgreSQL. (#111)
+- Fix for the [specificType](#Schema-specificType) schema call (#118)
+- Several fixes for migrations, including migration file path fixes, passing a Promise constructor to the migration `up` and `down` methods, allowing the "knex" module to be used globally, file ordering on migrations, and other small improvements. (#112-115, #125, #135)
+
+# 0.5.0 - Nov 25, 2013
+
+- Initial pass at a [migration](#Migrations) api.
+- Aggregate methods are no longer aliased as "aggregate", but may now be aliased and have more than one aggregate in a query (#108, #110).
+- Adding bigint and bigserial to PostgreSQL (#111).
+- Bugfix on increment/decrement values (#100).
+- Bugfix with having method (#107).
+- Switched from when.js to [bluebird](https://github.com/petkaantonov/bluebird) for promise implementation, with shim for backward compatibility.
+- Switched from underscore to lodash, for semver reliability
+
+# 0.4.13 - Oct 31, 2013
+
+- Fix for aggregate methods on toString and clone, (#98)
+
+# 0.4.12 - Oct 29, 2013
+
+- Fix incorrect values passed to float in MySQL and decimal in PostgreSQL
+
+# 0.4.11 - Oct 15, 2013
+
+- Fix potential sql injection vulnerability in orderBy, thanks to @sebgie
+
+# 0.4.10 - Oct 14, 2013
+
+- Added [forUpdate](#Builder-forUpdate) and [forShare](#Builder-forShare) for select modes in transactions. (#84)
+- Fix bug where current query chain type is not copied on [clone](#Builder-clone). (#90)
+- Charset and collate are now added as methods on the schema builder. (#89)
+- Added `into` as an alias of [from](#Builder-from), for builder syntax of: `insert(value).into(tableName)`
+- Internal pool fixes. (#90)
+
+# 0.4.9 - Oct 7, 2013
+
+- Fix for documentation of [hasColumn](#Schema-hasColumn), ensure that `hasColumn` works with MySQL (#87).
+- More cleanup of error messages, showing the original error message concatenated with the sql and bindings
+
+# 0.4.8 - Oct 2, 2013
+
+- Connections are no longer pushed back into the pool if they never existed to begin with (#85)
+
+# 0.4.7 - Sep 27, 2013
+
+- The column is now a documented method on the builder api, and takes either an individual column or an array of columns to select
+
+# 0.4.6 - Sep 25, 2013
+
+- Standardizing handling of errors for easier debugging, as noted in (#39)
+
+# 0.4.5 - Sep 24, 2013
+
+- Fix for hasTable always returning true in MySQL (#82), fix where sql queries were duplicated with multiple calls on toSql with the schema builder
+
+# 0.4.4 - Sep 22, 2013
+
+- Fix for debug method not properly debugging individual queries
+
+# 0.4.3 - Sep 18, 2013
+
+- Fix for underscore not being defined in various grammar files
+
+# 0.4.2 - Sep 17, 2013
+
+- Fix for an error being thrown when an initialized ClientBase instance was passed into Knex.initialize. pool.destroy now optionally accepts a callback to notify when it has completed draining and destroying all connections
+
+# 0.4.1 - Sep 16, 2013
+
+- Cleanup from the 0.4.0 release, fix a potential exploit in "where" clauses pointed out by Andri Möll, fix for clients not being properly released from the pool #70, fix for where("foo", "<>", null) doing an "IS NULL" statement
+
+# 0.4.0 - Sep 13, 2013
+
+### Breaking Changes:
+
+- Global state is no longer stored in the library, an instance is returned from `Knex.initialize`, so you will need to call this once and then reference this `knex` client elsewhere in your application.
+- Lowercasing of `knex.raw`, `knex.transaction`, and `knex.schema`.
+- Created columns are now nullable by default, unless `notNullable` is chained as an option.
+- Keys created with `increments` are now assumed to be unsigned (MySQL) by default.
+- The `destroyAllNow` is no longer called by the library on `process.exit` event. If you need to call it explicitly yourself, you may use `knex.client.destroyPool`
+
+# 0.2.6 - Aug 29, 2013
+
+- Reject the transaction promise if the transaction "commit" fails, (#50)
+
+# 0.2.5 - Aug 25, 2013
+
+- Fix error if a callback isn't specified for exec, (#49)
+
+# 0.2.4 - Aug 22, 2013
+
+- Fix SQLite3 delete not returning affected row count, (#45)
+
+# 0.2.3 - Aug 22, 2013
+
+- Fix insert with default values in PostgreSQL and SQLite3, (#44)
+
+# 0.2.2 - Aug 20, 2013
+
+- Allowing Raw queries to be passed as the primary table names
+
+# 0.2.1 - Aug 13, 2013
+
+- Fix for an array passed to insert being mutated
+
+# 0.2.0 - Aug 7, 2013
+
+### Breaking changes:
+
+- [hasTable](#Schema-hasTable) now returns a boolean rather than a failed promise.
+- Changed syntax for insert in postgresql, where the `id` is not assumed on inserts (#18). The second parameter of [insert](#Builder-insert) is now required to return an array of insert id's for the last insert.
+- The [timestamp](#Schema-timestamp) method on the schema builder now uses a `dateTime` rather than a `timestamp`
+
+# 0.1.8 - July 7, 2013
+
+- Somehow missing the != operator. Using _.find rather than _.where in getCommandsByName(#22)
+
+# 0.1.7 - June 12, 2013
+
+- Ensures unhandled errors in the exec callback interface are re-thrown
+
+# 0.1.6 - June 9, 2013
+
+- Renaming beforeCreate to afterCreate. Better handling of errors in the connection pooling
+
+# 0.1.5 - June 9, 2013
+
+- Added the ability to specify beforeCreate and beforeDestroy hooks on the initialize's options.pool to perform any necessary database setup/teardown on connections before use (#14). where and having may now accept Knex.Raw instances, for consistency (#15). Added an orHaving method to the builder. The ability to specify bindings on Raw queries has been removed
+
+# 0.1.4 - May 22, 2013
+
+- defaultTo now accepts "false" for boolean columns, allows for empty strings as default values
+
+# 0.1.3 - May 18, 2013
+
+- Enabling table aliases (#11). Fix for issues with transactions not functioning (#12)
+
+# 0.1.2 - May 15, 2013
+
+- Bug fixes for groupBy (#7). Mysql using collation, charset config settings in createTable. Added engine on schemaBuilder specifier (#6). Other doc fixes, tests
+
+# 0.1.1 - May 14, 2013
+
+- Bug fixes for sub-queries, minor changes to initializing "main" instance, adding "pg" as a valid parameter for the client name in the connection settings
+
+# 0.1.0 - May 13, 2013
+
+- Initial Knex release
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/CONTRIBUTING.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/CONTRIBUTING.md
new file mode 100644
index 000000000..6540defd4
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/CONTRIBUTING.md
@@ -0,0 +1,194 @@
+## How to contribute to Knex.js
+
+- Make changes in the `/lib` directory.
+
+- Before sending a pull request for a feature or bug fix, be sure to have
+ [tests](https://github.com/knex/knex/tree/master/test). Every pull request that changes the queries should have
+ also **integration tests which are ran against real database** (in addition to unit tests which checks which kind of queries
+ are being created).
+
+- Use the same coding style as the rest of the
+ [codebase](https://github.com/knex/knex/blob/master/knex.js).
+
+- All pull requests should be made to the `master` branch.
+
+- Pull request description should have link to corresponding PR of documentation branch.
+
+- All pull requests that modify the public API should be updated in [types/index.d.ts](https://github.com/knex/knex/blob/master/types/index.d.ts)
+
+## Documentation
+
+Documentation is no longer maintained in knex master repository. All the documentation pull requests should be sent to https://github.com/knex/documentation
+
+Documentation pull requests should not be merged before knex version which has the new documented feature is released.
+
+## I would like to add support for new dialect to knex, is it possible?
+
+Currently there are already way too many dialects supported in `knex` and instead of adding new dialect to central codebase, all the dialects should be moved to separate npm packages out from `knex` core library with their respective maintainers and test suites.
+
+So if you like to write your own dialect, you can just inherit own dialect from knex base classes and use it by passing dialect to knex in knex configuration (https://runkit.com/embed/90b3cpyr4jh2):
+
+```js
+// simple dialect overriding sqlite3 dialect to use sqlite3-offline driver
+require('sqlite3-offline');
+const Knex = require('knex');
+
+const Dialect = require(`knex/lib/dialects/sqlite3/index.js`);
+Dialect.prototype._driver = () => require('sqlite3-offline');
+
+const knex = Knex({
+ client: Dialect,
+ connection: ':memory:',
+});
+
+console.log(knex.select(knex.raw(1)).toSQL());
+
+await knex.schema.createTable('fooobar', (t) => {
+ t.bigincrements('id');
+ t.string('data');
+});
+await knex('fooobar').insert({ data: 'nomnom' });
+
+console.log('Gimme all the data:', await knex('fooobar'));
+```
+
+## What is minimal code to reproduce bug and why I have to provide that when I can just tell whats the problem is
+
+Writing minimal reproduction code for the problem is time-consuming and sometimes it is also really hard, for
+example when the original code where the bug happens is written using express or mocha. So why is it necessary
+for me to commit so much time to it when the problem is in `knex`? Contributors should be grateful that I reported
+the bug I found.
+
+The point of runnable code to reproduce the problem is to easily verify that there really is a problem and that the one
+who did the report did nothing wrong (surprisingly often problem is in the user code). So instead of just description
+what to do the complete code encourages devs to actually test out that problem exists and start solving it and it
+saves lots of time.
+
+tl;dr list:
+
+1. Actually in most of the cases developer already figures out what was the problem when writing the minimal test case
+ or if there was problem how stuff was initialized or how async code was written it is easy to point out the problem.
+
+2. It motivates developer to actually try out if the bug really exist by not having to figure out from incomplete example
+ environment in which and how bug actually manifests.
+
+3. There are currently very few people fixing knex issues and if one has to put easily 15-30 minutes time to issue just
+ to see that I cannot reproduce this issue it just wastes development hours that were available for improving knex.
+
+Test case should initialize needed tables, insert needed data and fail...
+
+```js
+const knex = require('knex')({
+ client: 'pg',
+ connection: 'postgres:///knex_test'
+});
+
+async function main() {
+ await knex.schema.createTable(...);
+ await knex('table').insert({foo: 'bar}');
+ await knex.destroy();
+}
+
+main();
+```
+
+Usually issues without reproduction code available are just closed and if the same issue is reported multiple
+times maybe someone looks into it.
+
+One easy way to setup database for your reproduction is to use database from knex's docker-compose setup (npm run db:start) and by checking the connection settings from tests' `test/knexfile.js`.
+
+## Integration Tests
+
+### The Easy Way
+
+By default, Knex runs tests against sqlite3, postgresql, mysql, mysql2, mssql and oracledb drivers. All databases can be initialized and ran with docker.
+
+Docker databases can be started and initialized with:
+
+```bash
+npm run db:start
+```
+
+and stopped with:
+
+```bash
+npm run db:stop
+```
+
+In case you don't need all of the databases, you can use simplified dev Docker configuration that only runs PostgreSQL, by running `npm run db:start:postgres` and `npm run db:stop:postgres` accordingly.
+
+### Installing support for oracledb
+
+Oracle has started providing precompiled driver libs for all the platforms, which makes it viable to run oracle tests also locally against oracledb running in docker.
+
+Check message when running
+
+```bash
+npm install oracledb
+```
+
+and download driver library binary packages and unzip it to ~/lib directory.
+
+### Specifying Databases
+
+You can optionally specify which dialects to test using the `DB` environment variable. Values should be space separated and can include:
+
+- mysql
+- mysql2
+- postgres
+- sqlite3
+- oracledb
+- mssql
+
+```bash
+$ DB='postgres mysql' npm test
+```
+
+### Custom Configuration
+
+If you'd like to override the database configuration (to use a different host, for example), you can override the path to the [default test configuration](https://github.com/knex/knex/blob/master/test/knexfile.js) using the `KNEX_TEST` environment variable.
+
+```bash
+$ KNEX_TEST='./path/to/my/config.js' npm test
+```
+
+### Creating Postgres User
+
+If you are running tests against own local database one might need to setup test user and database for knex to connect.
+
+To create a new user, login to Postgres and use the following queries to add the user. This assumes you've already created the `knex_test` database.
+
+```
+CREATE ROLE postgres WITH LOGIN PASSWORD '';
+GRANT ALL PRIVILEGES ON DATABASE "knex_test" TO postgres;
+```
+
+Once this is done, check it works by attempting to login:
+
+```
+psql -h localhost -U postgres -d knex_test
+```
+
+## Typescript source files
+
+> TL;DR: Starting with release 2.0.0 Knex is adding support for Typescript source files. Thus to develop in this repo you will need to run `npm run build` each time you edit `.ts` files to generate the resulting `.js` files. This is automatically run whenever you run `npm install` or checkout a new Git branch so when developing in Javascript you don't have to worry about it. It is encouraged that new functionality and sources be written in Typescript but this is not required.
+
+Starting with release 2.0.0, Knex is support source additions in Typescript! This allows for better safety in the code added. However, pre-2.0.0 Knex was always written in pure Javascript and thus a "hybrid" approach is being used for 2.0.0 to allow for the new `.ts` files to exist along `.js` files that make up the majority of this repository.
+
+To develop in this repository use the `npm run build` and `npm run clean` commands to compile and delete the `.js` and related files from `.ts` files. If you wish to have the `tsc` compiled watch and recompile on changes then run `npm run build:ts -- --watch`. Note that for easy integration with Javascript the outputted files are done in a "side-by-side" manner meaning that `lib/foo/bar.ts` will result in `lib/foo/bar.js`. This is done automatically via the npm script command `"prepare"` whenever you run `npm install` and Git hook for `post-checkout` (added by Husky) which executes when you run commands like `git checkout` , thus making it easier to not have to worry about this if you're working in pure Javascript.
+
+The script file `./scripts/update_gitignore_for_tsc_output.js` file is called as part of the `npm run build` command which will update the `lib/.gitignore` file which is used to ensure generated `.js` and related files from `tsc` compilation are not checked into the git repo.
+
+## Want to be Collaborator?
+
+There is always room for more collaborators. Be active on resolving github issues / sending pull requests / reviewing code and we will ask you to join.
+
+### Etiquette (/ˈɛtᵻkɛt/ or /ˈɛtᵻkɪt/, French: [e.ti.kɛt])
+
+Make pull requests for your changes, do not commit directly to master (release stuff like fixing changelog are ok though).
+
+All the pull requests must be peer reviewed by other collaborator, so don't merge your request before that. If there is no response ping others.
+
+If you are going to add new feature to knex (not just a bugfix) it should be discussed first with others to agree on details.
+
+Join Gitter chat if you feel to chat outside of github issues.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/LICENSE b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/LICENSE
new file mode 100644
index 000000000..9c8ee26d6
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013-present Tim Griesser
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/README.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/README.md
new file mode 100644
index 000000000..a019bdc9f
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/README.md
@@ -0,0 +1,149 @@
+# [knex.js](https://knex.github.io/documentation/)
+
+[](https://npmjs.org/package/knex)
+[](https://npmjs.org/package/knex)
+
+[](https://coveralls.io/r/knex/knex?branch=master)
+[](https://libraries.io/npm/knex)
+[](https://gitter.im/tgriesser/knex)
+
+> **A SQL query builder that is _flexible_, _portable_, and _fun_ to use!**
+
+A batteries-included, multi-dialect (PostgreSQL, MySQL, CockroachDB, MSSQL, SQLite3, Oracle (including Oracle Wallet Authentication)) query builder for
+Node.js, featuring:
+
+- [transactions](https://knex.github.io/documentation/#Transactions)
+- [connection pooling](https://knex.github.io/documentation/#Installation-pooling)
+- [streaming queries](https://knex.github.io/documentation/#Interfaces-Streams)
+- both a [promise](https://knex.github.io/documentation/#Interfaces-Promises) and [callback](https://knex.github.io/documentation/#Interfaces-Callbacks) API
+- a [thorough test suite](https://github.com/knex/knex/actions)
+
+Node.js versions 12+ are supported.
+
+- Take a look at the [full documentation](https://knex.github.io/documentation) to get started!
+- Browse the [list of plugins and tools](https://github.com/knex/knex/blob/master/ECOSYSTEM.md) built for knex
+- Check out our [recipes wiki](https://github.com/knex/knex/wiki/Recipes) to search for solutions to some specific problems
+- In case of upgrading from an older version, see [migration guide](https://github.com/knex/knex/blob/master/UPGRADING.md)
+
+You can report bugs and discuss features on the [GitHub issues page](https://github.com/knex/knex/issues) or send tweets to [@kibertoad](http://twitter.com/kibertoad).
+
+For support and questions, join our [Gitter channel](https://gitter.im/tgriesser/knex).
+
+For knex-based Object Relational Mapper, see:
+
+- https://github.com/Vincit/objection.js
+- https://github.com/mikro-orm/mikro-orm
+- https://bookshelfjs.org
+
+To see the SQL that Knex will generate for a given query, you can use [Knex Query Lab](https://michaelavila.com/knex-querylab/)
+
+## Examples
+
+We have several examples [on the website](http://knexjs.org). Here is the first one to get you started:
+
+```js
+const knex = require('knex')({
+ client: 'sqlite3',
+ connection: {
+ filename: './data.db',
+ },
+});
+
+try {
+ // Create a table
+ await knex.schema
+ .createTable('users', (table) => {
+ table.increments('id');
+ table.string('user_name');
+ })
+ // ...and another
+ .createTable('accounts', (table) => {
+ table.increments('id');
+ table.string('account_name');
+ table.integer('user_id').unsigned().references('users.id');
+ });
+
+ // Then query the table...
+ const insertedRows = await knex('users').insert({ user_name: 'Tim' });
+
+ // ...and using the insert id, insert into the other table.
+ await knex('accounts').insert({
+ account_name: 'knex',
+ user_id: insertedRows[0],
+ });
+
+ // Query both of the rows.
+ const selectedRows = await knex('users')
+ .join('accounts', 'users.id', 'accounts.user_id')
+ .select('users.user_name as user', 'accounts.account_name as account');
+
+ // map over the results
+ const enrichedRows = selectedRows.map((row) => ({ ...row, active: true }));
+
+ // Finally, add a catch statement
+} catch (e) {
+ console.error(e);
+}
+```
+
+## TypeScript example
+
+```ts
+import { Knex, knex } from 'knex';
+
+interface User {
+ id: number;
+ age: number;
+ name: string;
+ active: boolean;
+ departmentId: number;
+}
+
+const config: Knex.Config = {
+ client: 'sqlite3',
+ connection: {
+ filename: './data.db',
+ },
+};
+
+const knexInstance = knex(config);
+
+try {
+ const users = await knex('users').select('id', 'age');
+} catch (err) {
+ // error handling
+}
+```
+
+## Usage as ESM module
+
+If you are launching your Node application with `--experimental-modules`, `knex.mjs` should be picked up automatically and named ESM import should work out-of-the-box.
+Otherwise, if you want to use named imports, you'll have to import knex like this:
+
+```js
+import { knex } from 'knex/knex.mjs';
+```
+
+You can also just do the default import:
+
+```js
+import knex from 'knex';
+```
+
+If you are not using TypeScript and would like the IntelliSense of your IDE to work correctly, it is recommended to set the type explicitly:
+
+```js
+/**
+ * @type {Knex}
+ */
+const database = knex({
+ client: 'mysql',
+ connection: {
+ host: '127.0.0.1',
+ user: 'your_database_user',
+ password: 'your_database_password',
+ database: 'myapp_test',
+ },
+});
+database.migrate.latest();
+```
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/UPGRADING.md b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/UPGRADING.md
new file mode 100644
index 000000000..9bd3ef8fd
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/UPGRADING.md
@@ -0,0 +1,245 @@
+## Upgrading to new knex.js versions
+
+### Upgrading to version 2.0.0+
+
+- Since `sqlite3` is maintained again, we switched back to it. If you are using `@vscode/sqlite3` driver dependency, please replace it with `sqlite3` in your `package.json`;
+
+### Upgrading to version 1.0.0+
+
+- Node.js older than 12 is no longer supported, make sure to update your environment;
+- If you are using `sqlite3` driver dependency, please replace it with `@vscode/sqlite3` in your `package.json`;
+- `RETURNING` operations now always return an object with column names;
+- Migrator now returns list of migrations as objects.
+
+### Upgrading to version 0.95.0+
+
+- TypeScript type exports changed significantly. While `import Knex from 'knex';` used to import the knex instantiation function, the namespace and the interface for the knex instantiation function/object, there is now a clear distinction between them:
+
+```typescript
+import { knex } from 'knex'; // this is a function that you call to instantiate knex
+import { Knex } from 'knex'; // this is a namespace, and a type of a knex object
+import KnexTimeoutError = Knex.KnexTimeoutError; // this is a class from the Knex namespace
+
+const config: Knex.Config = {}; // this is a type from the Knex namespace
+const knexInstance: Knex = knex(config);
+```
+
+If your code looked like this:
+
+```typescript
+import knex from 'knex';
+
+const config: knex.Config = {}; // this is a type from the Knex namespace
+const knexInstance = knex(config);
+```
+
+Change it to
+
+```typescript
+import { knex, Knex } from 'knex';
+
+const config: Knex.Config = {}; // this is a type from the Knex namespace
+const knexInstance = knex(config);
+```
+
+- If you were importing types such as `Config` or `QueryBuilder` directly, use `Knex` namespace instead.
+
+So change this:
+
+```ts
+import { QueryBuilder } from 'knex';
+
+const qb: QueryBuilder = knex('table').select('*');
+```
+
+to this:
+
+```ts
+import { Knex } from 'knex';
+
+const qb: Knex.QueryBuilder = knex('table').select('*');
+```
+
+- IDE autocomplete may stop working if you are using JavaScript (not TypeScript). There are reports for autocomplete still working correctly if knex is used this way:
+
+```js
+const knex = require('knex').knex({
+ //connection parameters
+});
+```
+
+It also works when using ESM imports:
+
+```js
+import { knex } from 'knex';
+
+const kn = knex({
+ //connection parameters
+});
+```
+
+For usage as param it can be addressed like this:
+
+```js
+/**
+ * @param {import("knex").Knex} db
+ */
+function up(db) {
+ // Your code
+}
+```
+
+- Syntax for QueryBuilder augmentation changed. Previously it looked like this:
+
+```ts
+declare module 'knex' {
+ interface QueryBuilder {
+ paginate(
+ params: IPaginateParams
+ ): KnexQB>;
+ }
+}
+```
+
+This should be changed into this:
+
+```ts
+declare module 'knex' {
+ namespace Knex {
+ interface QueryBuilder {
+ paginate(
+ params: IPaginateParams
+ ): KnexQB>;
+ }
+ }
+}
+```
+
+- TypeScript version 4.1+ is needed when using knex types now.
+
+- MSSQL driver was completely reworked in order to address the multitude of connection pool, error handling and performance issues. Since the new implementation uses `tedious` library directly instead of `mssql`, please replace `mssql` with `tedious` in your dependencies if you are using a MSSQL database.
+
+- Transaction rollback does not trigger a promise rejection for transactions with specified handler. If you want to preserve previous behavior, pass `config` object with `doNotRejectOnRollback: false`:
+
+```js
+await knex.transaction(
+ async (trx) => {
+ const ids = await trx('catalogues').insert({ name: 'Old Books' }, 'id');
+ },
+ { doNotRejectOnRollback: false }
+);
+```
+
+- Connection url parsing changed from legacy [url.parse](https://nodejs.org/docs/latest-v10.x/api/url.html#url_legacy_url_api) to [WHATWG URL](https://nodejs.org/docs/latest-v10.x/api/url.html#url_the_whatwg_url_api). If you have symbols, unusual for a URL (not A-z, not digits, not dot, not dash) - check [Node.js docs](https://nodejs.org/docs/latest-v10.x/api/url.html#url_percent_encoding_in_urls) for details
+
+- Global static `Knex.raw` support dropped, use instance `knex.raw` instead. (`require('knex').raw()` won't work anymore)
+
+- v8 flags are no longer supported in cli. To pass these flags use [`NODE_OPTIONS` environment variable](https://nodejs.org/api/cli.html#cli_node_options_options).
+ For example `NODE_OPTIONS="--max-old-space-size=1536" npm run knex`
+
+- Clients are now classes instead of new-able functions. Please migrate your custom clients to classes.
+
+```js
+const Client = require('knex');
+const { inherits } = require('util');
+
+// old
+function CustomClient(config) {
+ Client.call(this, config);
+ // construction logic
+}
+inherits(CustomClient, Client);
+CustomClient.prototype.methodOverride = function () {
+ // logic
+};
+
+// new
+class CustomClient extends Client {
+ // node 12+
+ driverName = 'abcd';
+ constructor(config) {
+ super(config);
+ this.driverName = 'abcd'; // bad way, will not work
+ // construction logic
+ }
+ methodOverride() {
+ // logic
+ }
+}
+// alternative to declare driverName
+CustomClient.prototype.driverName = 'abcd';
+```
+
+- There was a major internal restructuring and renaming effort. Most dialect-specific compilers/builder have dialect name as a prefix now. Also some files were moved. Make sure to make adjustments accordingly if you were referencing specific knex library files directly from your code.
+
+- "first" and "pluck" can no longer be both chained on the same operation. Previously only the last one chained was used, now this would throw an error.
+
+- Trying to execute an operation resulting in an empty query such as inserting an empty array, will now throw an error on all database drivers.
+
+### Upgrading to version 0.21.0+
+
+- Node.js older than 10 is no longer supported, make sure to update your environment;
+
+### Upgrading to version 0.19.0+
+
+- Passing unknown properties to connection pool configuration now throws errors (see https://github.com/Vincit/tarn.js/issues/19 for details);
+- `beforeDestroy` pool configuration option was removed. You should use tarn.js event handlers if you still need similar functionality.
+
+### Upgrading to version 0.18.0+
+
+- Node.js older than 8 is no longer supported, make sure to update your environment;
+- Knex returns native promises instead of bluebird ones now. You will need to update your code not to rely on bluebird-specific functionality;
+- Knex.Promise was removed, use native promises;
+- Promise is no longer passed to migrations and seeds, use native one;
+- If you are using TypeScript, make sure to include 'es6' in compilerOptions.lib, otherwise you may get errors for methods `.catch()` and `then()` not being recognized.
+
+### Upgrading to version 0.17.0+
+
+- Generic support was implemented for TypeScript bindings, which may break TS builds in some edge cases. Please refer to https://knexjs.org/#typescript-support for more elaborate documentation.
+
+### Upgrading to version 0.16.0+
+
+- MSSQL: DB versions older than 2008 are no longer supported, make sure to update your DB;
+- PostgreSQL|MySQL: it is recommended to use options object for `table.datetime` and `table.timestamp` methods instead of argument options. See documentation for these methods for more details;
+- Node 6: There are known issues with duplicate event listeners when using knex.js with Node.js 6 (resulting in MaxListenersExceededWarning under certain use-cases (such as reusing single knex instance to run migrations or seeds multiple times)). Please upgrade to Node.js 8+ as soon as possible (knex 0.17.0 will be dropping Node.js 6 support altogether);
+
+### Upgrading to version 0.15.0+
+
+- Node.js older than 6 is no longer supported, make sure to update your environment;
+
+- MSSQL: Creating a unique index on the table targeted by stored procedures that were created with QUOTED_IDENTIFIER = OFF fails.
+
+You can use this query to identify all affected stored procedures:
+
+```
+SELECT name = OBJECT_NAME([object_id]), uses_quoted_identifier
+FROM sys.sql_modules
+WHERE uses_quoted_identifier = 0;
+```
+
+The only known solution is to recreate all stored procedures with QUOTED_IDENTIFIER = OFF
+
+- MariaDB: `mariadb` dialect is no longer supported;
+
+Instead, use "mysql" or "mysql2" dialects.
+
+### Upgrading to version 0.14.4+
+
+- Including schema in tableName parameter in migrations no longer works, so this is invalid:
+
+```js
+await knex.migrate.latest({
+ directory: 'src/services/orders/database/migrations',
+ tableName: 'orders.orders_migrations',
+});
+```
+
+Instead, starting from 0.14.5 you should use new parameter schemaName:
+
+```js
+await knex.migrate.latest({
+ directory: 'src/services/orders/database/migrations',
+ tableName: 'orders_migrations',
+ schemaName: 'orders',
+});
+```
diff --git a/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/bin/cli.js b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/bin/cli.js
new file mode 100644
index 000000000..70fe701cd
--- /dev/null
+++ b/courses/foundation/intro-to-backend/introToBackendAssignment/node_modules/knex/bin/cli.js
@@ -0,0 +1,475 @@
+#!/usr/bin/env node
+const rechoir = require('rechoir');
+const merge = require('lodash/merge');
+const interpret = require('interpret');
+const resolveFrom = require('resolve-from');
+const path = require('path');
+const tildify = require('tildify');
+const commander = require('commander');
+const color = require('colorette');
+const argv = require('getopts')(process.argv.slice(2));
+const cliPkg = require('../package');
+const {
+ parseConfigObj,
+ mkConfigObj,
+ resolveEnvironmentConfig,
+ exit,
+ success,
+ checkLocalModule,
+ checkConfigurationOptions,
+ getMigrationExtension,
+ getSeedExtension,
+ getStubPath,
+ findUpModulePath,
+ findUpConfig,
+} = require('./utils/cli-config-utils');
+const {
+ existsSync,
+ readFile,
+ writeFile,
+} = require('../lib/migrations/util/fs');
+
+const { listMigrations } = require('./utils/migrationsLister');
+
+async function openKnexfile(configPath) {
+ const importFile = require('../lib/migrations/util/import-file'); // require me late!
+ let config = await importFile(configPath);
+ if (config && config.default) {
+ config = config.default;
+ }
+ if (typeof config === 'function') {
+ config = await config();
+ }
+ return config;
+}
+
+async function initKnex(env, opts, useDefaultClientIfNotSpecified) {
+ checkLocalModule(env);
+ if (process.cwd() !== env.cwd) {
+ process.chdir(env.cwd);
+ console.log(
+ 'Working directory changed to',
+ color.magenta(tildify(env.cwd))
+ );
+ }
+
+ if (!useDefaultClientIfNotSpecified) {
+ checkConfigurationOptions(env, opts);
+ }
+
+ env.configuration = env.configPath
+ ? await openKnexfile(env.configPath)
+ : mkConfigObj(opts);
+
+ const resolvedConfig = resolveEnvironmentConfig(
+ opts,
+ env.configuration,
+ env.configPath
+ );
+
+ const optionsConfig = parseConfigObj(opts);
+ const config = merge(resolvedConfig, optionsConfig);
+
+ // Migrations directory gets defaulted if it is undefined.
+ if (!env.configPath && !config.migrations.directory) {
+ config.migrations.directory = null;
+ }
+
+ // Client gets defaulted if undefined and it's allowed
+ if (useDefaultClientIfNotSpecified && config.client === undefined) {
+ config.client = 'sqlite3';
+ }
+
+ const knex = require(env.modulePath);
+ return knex(config);
+}
+
+function invoke() {
+ const filetypes = ['js', 'mjs', 'coffee', 'ts', 'eg', 'ls'];
+
+ const cwd = argv.knexfile
+ ? path.dirname(path.resolve(argv.knexfile))
+ : process.cwd();
+
+ // TODO add knexpath here eventually
+ const modulePath =
+ resolveFrom.silent(cwd, 'knex') ||
+ findUpModulePath(cwd, 'knex') ||
+ process.env.KNEX_PATH;
+
+ const configPath =
+ argv.knexfile && existsSync(argv.knexfile)
+ ? path.resolve(argv.knexfile)
+ : findUpConfig(cwd, 'knexfile', filetypes);
+
+ if (configPath) {
+ const autoloads = rechoir.prepare(
+ interpret.jsVariants,
+ configPath,
+ cwd,
+ true
+ );
+ if (autoloads instanceof Error) {
+ // Only errors
+ autoloads.failures.forEach(function (failed) {
+ console.log(
+ color.red('Failed to load external module'),
+ color.magenta(failed.moduleName)
+ );
+ });
+ } else if (Array.isArray(autoloads)) {
+ const succeeded = autoloads[autoloads.length - 1];
+ console.log(
+ 'Requiring external module',
+ color.magenta(succeeded.moduleName)
+ );
+ }
+ }
+
+ const env = {
+ cwd,
+ modulePath,
+ configPath,
+ configuration: null,
+ };
+
+ let modulePackage = {};
+ try {
+ modulePackage = require(path.join(
+ path.dirname(env.modulePath),
+ 'package.json'
+ ));
+ } catch (e) {
+ /* empty */
+ }
+
+ const cliVersion = [
+ color.blue('Knex CLI version:'),
+ color.green(cliPkg.version),
+ ].join(' ');
+
+ const localVersion = [
+ color.blue('Knex Local version:'),
+ color.green(modulePackage.version || 'None'),
+ ].join(' ');
+
+ commander
+ .version(`${cliVersion}\n${localVersion}`)
+ .option('--debug', 'Run with debugging.')
+ .option('--knexfile [path]', 'Specify the knexfile path.')
+ .option('--knexpath [path]', 'Specify the path to knex instance.')
+ .option('--cwd [path]', 'Specify the working directory.')
+ .option('--client [name]', 'Set DB client.')
+ .option('--connection [address]', 'Set DB connection.')
+ .option('--migrations-directory [path]', 'Set migrations directory.')
+ .option('--migrations-table-name [path]', 'Set migrations table name.')
+ .option(
+ '--env [name]',
+ 'environment, default: process.env.NODE_ENV || development'
+ )
+ .option('--esm', 'Enable ESM interop.')
+ .option('--specific [path]', 'Specify one seed file to execute.')
+ .option(
+ '--timestamp-filename-prefix',
+ 'Enable a timestamp prefix on name of generated seed files.'
+ );
+
+ commander
+ .command('init')
+ .description(' Create a fresh knexfile.')
+ .option(
+ `-x [${filetypes.join('|')}]`,
+ 'Specify the knexfile extension (default js)'
+ )
+ .action(() => {
+ const type = (argv.x || 'js').toLowerCase();
+ if (filetypes.indexOf(type) === -1) {
+ exit(`Invalid filetype specified: ${type}`);
+ }
+ if (env.configuration) {
+ exit(`Error: ${env.knexfile} already exists`);
+ }
+ checkLocalModule(env);
+ const stubPath = `./knexfile.${type}`;
+ readFile(
+ path.dirname(env.modulePath) +
+ '/lib/migrations/migrate/stub/knexfile-' +
+ type +
+ '.stub'
+ )
+ .then((code) => {
+ return writeFile(stubPath, code);
+ })
+ .then(() => {
+ success(color.green(`Created ${stubPath}`));
+ })
+ .catch(exit);
+ });
+
+ commander
+ .command('migrate:make ')
+ .description(' Create a named migration file.')
+ .option(
+ `-x [${filetypes.join('|')}]`,
+ 'Specify the stub extension (default js)'
+ )
+ .option(
+ `--stub [