filesystem-sandbox
Advanced tools
Comparing version 1.19.0 to 1.19.1
@@ -1,44 +0,2 @@ | ||
/// <reference types="node" /> | ||
import { StatsBase } from "fs"; | ||
export declare type Func<T> = () => T; | ||
export declare type AsyncFunc<T> = () => Promise<T>; | ||
export declare const basePrefix = "__sandboxes__"; | ||
export declare class Sandbox { | ||
private readonly _path; | ||
private readonly _base; | ||
private readonly _at; | ||
get path(): string; | ||
constructor(at?: string); | ||
destroy(): Promise<void>; | ||
private tryDestroyAt; | ||
private tryDestroyContainer; | ||
private destroyFolderIfEmpty; | ||
private tryDestroySelf; | ||
writeFile(at: string, contents: string | Buffer | string[]): Promise<string>; | ||
appendFile(at: string, contents: string | Buffer | string[]): Promise<string>; | ||
mkdir(name: string): Promise<string>; | ||
private resolveRelativePath; | ||
readTextFile(at: string): Promise<string>; | ||
readFile(at: string): Promise<Buffer>; | ||
fullPathFor(relativePath: string, ...parts: string[]): string; | ||
stat(at: string): Promise<StatsBase<any> | null>; | ||
folderExists(at: string): Promise<boolean>; | ||
fileExists(at: string): Promise<boolean>; | ||
private runOnStat; | ||
run<T>(fn: Func<T> | AsyncFunc<T>, relativePath?: string): Promise<T>; | ||
private _validatePathInsideSandbox; | ||
/** | ||
* Destroys all sandboxes created in this process up until now | ||
*/ | ||
static destroyAll(): Promise<void>; | ||
/** | ||
* Destroys the default base folder for sandboxes -- useful as a once-off | ||
* run before all tests to ensure that there are no lingering sandboxes | ||
* - cannot destroy sandboxes in custom paths | ||
*/ | ||
static destroyAny(): Promise<void>; | ||
static create(at?: string): Promise<Sandbox>; | ||
static setBaseTargetToCurrentWorkingDirectory(): Promise<void>; | ||
static setBaseTargetToSystemTempFolder(): Promise<void>; | ||
static setBaseTarget(to: string): Promise<void>; | ||
} | ||
export * from "./filesystem-sandbox"; | ||
export * from "./index"; |
@@ -9,240 +9,8 @@ "use strict"; | ||
})); | ||
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 (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Sandbox = exports.basePrefix = void 0; | ||
const fs_1 = require("fs"); | ||
const os = __importStar(require("os")); | ||
if (!fs_1.promises) { | ||
throw new Error("Yer node is olde! filesystem-sandboxes requires a Node with fs.promises"); | ||
} | ||
// require for "path" produces the cleanest js output | ||
const path_1 = __importDefault(require("path")); | ||
const uuid_1 = require("uuid"); | ||
const mkdirp_1 = require("mkdirp"); | ||
const rimraf_1 = require("rimraf"); | ||
const { writeFile, readFile, stat, appendFile } = fs_1.promises; | ||
async function isFolder(p) { | ||
try { | ||
const st = await stat(p); | ||
return st && st.isDirectory(); | ||
} | ||
catch (e) { | ||
return false; | ||
} | ||
} | ||
async function readdir(p) { | ||
const exists = await isFolder(p); | ||
if (!exists) { | ||
return []; | ||
} | ||
return await fs_1.promises.readdir(p); | ||
} | ||
async function rimraf(p) { | ||
if (!(await isFolder(p))) { | ||
return; | ||
} | ||
return new Promise((resolve, reject) => { | ||
try { | ||
rimraf_1.sync(p, { maxBusyTries: 5 }); | ||
resolve(); | ||
} | ||
catch (e) { | ||
reject(e); | ||
} | ||
}); | ||
} | ||
exports.basePrefix = "__sandboxes__"; | ||
let baseTarget = os.tmpdir(); | ||
const sandboxes = []; | ||
async function writeData(fullPath, contents, writeFunction) { | ||
const options = contents instanceof Buffer | ||
? undefined | ||
: { encoding: "utf8" }; | ||
if (Array.isArray(contents)) { | ||
contents = contents.join("\n"); | ||
} | ||
await writeFunction(fullPath, contents, options); | ||
return fullPath; | ||
} | ||
class Sandbox { | ||
constructor(at) { | ||
this._at = at; | ||
this._base = path_1.default.join(at || baseTarget, exports.basePrefix); | ||
this._path = path_1.default.join(this._base, uuid_1.v4()); | ||
mkdirp_1.sync(this._path); | ||
sandboxes.push(this); | ||
} | ||
get path() { | ||
return this._path; | ||
} | ||
async destroy() { | ||
await this.tryDestroySelf(); | ||
await this.tryDestroyContainer(); | ||
await this.tryDestroyAt(); | ||
} | ||
async tryDestroyAt() { | ||
if (!this._at) { | ||
return; | ||
} | ||
await this.destroyFolderIfEmpty(this._at); | ||
} | ||
async tryDestroyContainer() { | ||
await this.destroyFolderIfEmpty(this._base); | ||
} | ||
async destroyFolderIfEmpty(p) { | ||
const contents = await readdir(p); | ||
if (!contents.length) { | ||
await rimraf(p); | ||
} | ||
} | ||
async tryDestroySelf() { | ||
const myIndex = sandboxes.indexOf(this); | ||
if (myIndex > -1) { | ||
sandboxes.splice(myIndex, 1); | ||
} | ||
await rimraf(this._path); | ||
} | ||
async writeFile(at, contents) { | ||
try { | ||
await this.mkdir(path_1.default.dirname(at)); | ||
return await writeData(this.fullPathFor(at), contents, writeFile); | ||
} | ||
catch (e) { | ||
throw new Error(`Unable to write file at ${at}: ${e.message || e}`); | ||
} | ||
} | ||
async appendFile(at, contents) { | ||
try { | ||
await this.mkdir(path_1.default.dirname(at)); | ||
return await writeData(this.fullPathFor(at), contents, appendFile); | ||
} | ||
catch (e) { | ||
throw new Error(`Unable to append to file at ${at}: ${e.message || e}`); | ||
} | ||
} | ||
async mkdir(name) { | ||
name = this.resolveRelativePath(name); | ||
return new Promise(async (resolve, reject) => { | ||
const fullpath = path_1.default.join(this._path, name); | ||
if (await isFolder(fullpath)) { | ||
resolve(fullpath); | ||
} | ||
try { | ||
mkdirp_1.sync(fullpath); | ||
resolve(fullpath); | ||
} | ||
catch (e) { | ||
reject(e); | ||
} | ||
}); | ||
} | ||
resolveRelativePath(at) { | ||
if (!path_1.default.isAbsolute(at)) { | ||
return at; | ||
} | ||
const result = path_1.default.relative(this._path, at); | ||
if (result.startsWith("..")) { | ||
throw new Error(`${at} is outside the sandbox at ${this._path}`); | ||
} | ||
return result; | ||
} | ||
async readTextFile(at) { | ||
return readFile(this.fullPathFor(at), { encoding: "utf8" }); | ||
} | ||
async readFile(at) { | ||
return readFile(this.fullPathFor(at)); | ||
} | ||
fullPathFor(relativePath, ...parts) { | ||
relativePath = this.resolveRelativePath(relativePath); | ||
return path_1.default.join(this._path, relativePath, ...parts); | ||
} | ||
async stat(at) { | ||
at = this.resolveRelativePath(at); | ||
try { | ||
return await fs_1.promises.stat(this.fullPathFor(at)); | ||
} | ||
catch (e) { | ||
return null; | ||
} | ||
} | ||
async folderExists(at) { | ||
at = this.resolveRelativePath(at); | ||
return this.runOnStat(at, st => !!st && st.isDirectory()); | ||
} | ||
async fileExists(at) { | ||
at = this.resolveRelativePath(at); | ||
return this.runOnStat(at, st => !!st && st.isFile()); | ||
} | ||
async runOnStat(relativePath, fn) { | ||
const st = await this.stat(relativePath); | ||
return fn(st); | ||
} | ||
async run(fn, relativePath) { | ||
const start = process.cwd(); | ||
try { | ||
const target = relativePath | ||
? path_1.default.resolve(path_1.default.join(this.path, relativePath)) | ||
: this.path; | ||
this._validatePathInsideSandbox(target); | ||
process.chdir(target); | ||
return await fn(); | ||
} | ||
finally { | ||
process.chdir(start); | ||
} | ||
} | ||
_validatePathInsideSandbox(t) { | ||
if (t.startsWith(this.path)) { | ||
return; | ||
} | ||
throw new Error(`${t} is not inside sandbox at ${this.path}`); | ||
} | ||
/** | ||
* Destroys all sandboxes created in this process up until now | ||
*/ | ||
static async destroyAll() { | ||
const toDestroy = sandboxes.splice(0, sandboxes.length); | ||
for (const sandbox of toDestroy) { | ||
await sandbox.destroy(); | ||
} | ||
} | ||
/** | ||
* Destroys the default base folder for sandboxes -- useful as a once-off | ||
* run before all tests to ensure that there are no lingering sandboxes | ||
* - cannot destroy sandboxes in custom paths | ||
*/ | ||
static async destroyAny() { | ||
const target = path_1.default.join(baseTarget, exports.basePrefix); | ||
await rimraf(target); | ||
} | ||
static async create(at) { | ||
return new Sandbox(at); | ||
} | ||
static async setBaseTargetToCurrentWorkingDirectory() { | ||
await this.setBaseTarget(process.cwd()); | ||
} | ||
static async setBaseTargetToSystemTempFolder() { | ||
await this.setBaseTarget(os.tmpdir()); | ||
} | ||
static async setBaseTarget(to) { | ||
if (!(await isFolder(to))) { | ||
await mkdirp_1.sync(to); | ||
} | ||
baseTarget = to; | ||
} | ||
} | ||
exports.Sandbox = Sandbox; | ||
// this is a generated file: do not edit | ||
__exportStar(require("./filesystem-sandbox"), exports); | ||
__exportStar(require("./index"), exports); |
{ | ||
"name": "filesystem-sandbox", | ||
"version": "1.19.0", | ||
"version": "1.19.1", | ||
"description": "JavaScript module to provide filesystem sandboxes for testing", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"files": [ | ||
"dist/**/*" | ||
"dist/**/*", | ||
"index.js", | ||
"index.d.ts" | ||
], | ||
"scripts": { | ||
"test": "jest", | ||
"prebuild": "rimraf dist", | ||
"prebuild": "run-p clean-dist generate-index", | ||
"clean-dist": "rimraf dist", | ||
"generate-index": "node generate-index.js", | ||
"build": "run-p tsc lint", | ||
@@ -55,3 +59,3 @@ "autobuild": "nodemon -V -w src -w tests -e ts -x \"run-s -s build autobuild-message\"", | ||
"typescript": "^3.9.3", | ||
"zarro": "^1.68.0" | ||
"zarro": "^1.78.0" | ||
}, | ||
@@ -58,0 +62,0 @@ "dependencies": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16754
9
310
1