Comparing version
111
index.js
@@ -1,9 +0,104 @@ | ||
module.exports = { | ||
bulk: require('./commands/bulk') | ||
, open: require('./commands/open') | ||
, link: require('./commands/link') | ||
, list: require('./commands/list') | ||
, purge: require('./commands/purge') | ||
, config: require('./commands/config') | ||
, expand: require('./commands/expand') | ||
const path = require('path'); | ||
const execa = require('execa'); | ||
const tmp = require('tmp'); | ||
// TODO: log in production with proces.env.DEBUG_APERTURE | ||
function log(...msgs) { | ||
if (process.env.DEBUG) { | ||
console.log(...msgs); | ||
} | ||
} | ||
class Aperture { | ||
getAudioSources() { | ||
return execa(path.join(__dirname, 'swift', 'main'), ['list-audio-devices']).then(result => { | ||
return JSON.parse(result.stdout); | ||
}); | ||
} | ||
// resolves if the recording started successfully | ||
// rejects if the recording didn't started after 5 seconds or if some error | ||
// occurs during the recording session | ||
startRecording({ | ||
fps = 30, | ||
cropArea = 'none', // can be 'none' or {x, y, width, height} – TODO: document this | ||
showCursor = true, | ||
highlightClicks = false, | ||
displayId = 'main', | ||
audioSourceId = 'none' // one of the `id`s from getAudioSources() | ||
} = {}) { | ||
return new Promise((resolve, reject) => { | ||
this.tmpPath = tmp.tmpNameSync({postfix: '.mp4'}); | ||
if (typeof cropArea === 'object') { // TODO validate this | ||
cropArea = `${cropArea.x}:${cropArea.y}:${cropArea.width}:${cropArea.height}`; | ||
} | ||
const recorderOpts = [this.tmpPath, fps, cropArea, showCursor, highlightClicks, displayId, audioSourceId]; | ||
this.recorder = execa(path.join(__dirname, 'swift', 'main'), recorderOpts); | ||
const timeout = setTimeout(() => { | ||
const err = new Error('unnable to start the recorder after 5 seconds'); | ||
err.code = 'RECORDER_TIMEOUT'; | ||
this.recorder.kill(); | ||
reject(err); | ||
}, 5000); | ||
this.recorder.stdout.on('data', data => { | ||
data = data.toString(); | ||
log(data); | ||
if (data.replace(/\n|\s/gm, '') === 'R') { | ||
// `R` is printed by Swift when the recording **actually** starts | ||
clearTimeout(timeout); | ||
resolve(this.tmpPath); | ||
} | ||
}); | ||
this.recorder.on('error', reject); // TODO handle this; | ||
this.recorder.on('exit', code => { | ||
clearTimeout(timeout); | ||
let err; | ||
if (code === 0) { | ||
return; // we're good | ||
} else if (code === 1) { | ||
err = new Error('malformed arguments'); // TODO | ||
} else if (code === 2) { | ||
err = new Error('invalid coordinates'); // TODO | ||
} else { | ||
err = new Error('unknown error'); // TODO | ||
} | ||
reject(err); | ||
}); | ||
}); | ||
} | ||
stopRecording() { | ||
return new Promise((resolve, reject) => { | ||
if (this.recorder === undefined) { | ||
reject('call `startRecording` first'); | ||
} | ||
this.recorder.on('exit', code => { | ||
// at this point the movie file has been fully written to the file system | ||
if (code === 0) { | ||
delete this.recorder; | ||
resolve(this.tmpPath); | ||
// TODO: this file is deleted when the program exits | ||
// maybe we should add a note about this on the docs or implement a workaround | ||
delete this.tmpPath; | ||
} else { | ||
reject(code); // TODO | ||
} | ||
}); | ||
this.recorder.kill(); | ||
}); | ||
} | ||
} | ||
module.exports = () => new Aperture(); |
{ | ||
"name": "aperture", | ||
"version": "1.1.1", | ||
"description": "Local dependencies helper", | ||
"main": "index.js", | ||
"version": "2.0.0", | ||
"description": "Record the screen on macOS", | ||
"license": "MIT", | ||
"repository": "wulkano/aperture", | ||
"author": "Matheus Fernandes <npm@matheus.top> (https://matheus.top)", | ||
"scripts": { | ||
"test": "tape test/*.js | tap-spec" | ||
"test": "xo", | ||
"build": "cd swift && xcodebuild && mv build/release/aperture main && rm -r build", | ||
"prepublish": "npm run build" | ||
}, | ||
"bin": { | ||
"aperture": "./aperture.js" | ||
}, | ||
"author": "Hugh Kennedy <hughskennedy@gmail.com> (http://hughsk.io/)", | ||
"contributors": [ | ||
"Tim Oxley" | ||
], | ||
"dependencies": { | ||
"flatten": "0.0.1", | ||
"once": "~1.3.0", | ||
"uniq": "0.0.2", | ||
"map-async": "~0.1.1", | ||
"rimraf": "~2.2.6", | ||
"mkdirp": "~0.3.5", | ||
"optimist": "~0.6.0", | ||
"readdirp": "~0.3.3", | ||
"through2": "~0.4.1", | ||
"globs": "~0.1.1", | ||
"findup": "~0.1.3", | ||
"async-series": "0.0.1", | ||
"pluck": "0.0.4", | ||
"chalk": "~0.4.0", | ||
"semver": "^2.2.1", | ||
"npm-stats": "^0.3.0", | ||
"map-limit": "0.0.0" | ||
"execa": "^0.5.0", | ||
"tmp": "0.0.31" | ||
}, | ||
"devDependencies": { | ||
"directory-copy": "~0.1.0", | ||
"quick-tmp": "0.0.0", | ||
"tap-spec": "^0.2.0", | ||
"tape": "~2.4.0" | ||
"xo": "^0.17.0" | ||
}, | ||
"directories": { | ||
"test": "test" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/requireio/aperture.git" | ||
}, | ||
"keywords": [ | ||
"npm", | ||
"link", | ||
"require", | ||
"development", | ||
"dev", | ||
"install", | ||
"dependency" | ||
"files": [ | ||
"index.js", | ||
"swift/main" | ||
], | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/requireio/aperture/issues" | ||
}, | ||
"homepage": "https://github.com/requireio/aperture" | ||
"xo": { | ||
"space": true, | ||
"esnext": true | ||
} | ||
} |
228
README.md
@@ -1,203 +0,123 @@ | ||
# aperture # | ||
<p> | ||
<h1 align="center">aperture</h1> | ||
<h3 align="center">A library for screen recording </h3> | ||
<p align="center"><a href="https://github.com/sindresorhus/xo"><img src="https://img.shields.io/badge/code_style-XO-5ed9c7.svg" alt="XO code style"></a></p> | ||
</p> | ||
Command-line tool to help with managing largeish amounts of local dependencies | ||
in `node_modules` for a single project. | ||
## Install | ||
## Usage ## | ||
``` bash | ||
npm install -g aperture | ||
``` | ||
npm install aperture | ||
``` | ||
Usage: | ||
aperture <command> [options] | ||
Commands: | ||
open links, installs and purges your dependencies for fresh projects. | ||
link Sets up the local links in the target directory. | ||
list Lists the modules configured to be linked. | ||
bulk Runs a shell command from each linked module. | ||
install Intelligently install your node dependencies for local development. | ||
config Print out the current config being used. | ||
expand Expand any globs present in `aperture.sources`. | ||
purge Permanently removes any module duplicates which should | ||
be linked in the tree. | ||
## Usage | ||
```javascript | ||
const aperture = require('aperture')(); | ||
Options: | ||
-b, --bail Exit early on reaching an error during "aperture bulk". | ||
-v, --version Output the current version and exit | ||
-d, --cwd Target a different directory for this command. | ||
Default: current directory | ||
``` | ||
const cropArea = {x: 100, y: 100, width: 500, height: 500}; | ||
### package.json ### | ||
aperture.startRecording({fps: 30, cropArea}) | ||
.then(filePath => setTimeout(stopRecording, 3000)); | ||
Configuration is added to a `package.json` file at the root of your project, | ||
e.g.: | ||
function stopRecording() { | ||
aperture.stopRecording() | ||
.then(console.log); //=> /var/folders/r9/65knbqts47x3yg055cd739qh0000gn/T/tmp-15694AAzbYX1vzi2X.mp4 | ||
} | ||
``` json | ||
{ | ||
"aperture": { | ||
"sources": [ | ||
"utils/custom-element", | ||
"utils/ajax-data", | ||
"features/*" | ||
] | ||
} | ||
} | ||
``` | ||
Where `aperture.sources` should be an array of package directories – globs are | ||
supported too. | ||
## API | ||
### aperture init ### | ||
### instance = aperture() | ||
Provided the configuration has been set up correctly, you can run | ||
this command to set up the dependencies for a fresh project. Essentially, | ||
it's the equivalent of this: | ||
### instance.startRecording([options]) | ||
``` bash | ||
aperture link && | ||
aperture bulk -- npm install --color=always && | ||
aperture purge | ||
``` | ||
Returns a promise for the path to the screen recording file. | ||
But is none-the-less included for convenience. For more flexibility and faster | ||
updates after the initial setup, the commands that follow are likely to be | ||
useful. | ||
### instance.stopRecording() | ||
### aperture link ### | ||
Returns a promise for the path to the screen recording file. | ||
Now, to symlink these directories to the top-level, just run this for your | ||
project's root: | ||
### instance.getAudioSources() | ||
``` bash | ||
aperture link | ||
Get a list of audio sources. | ||
Example: | ||
```js | ||
[{ | ||
id: 'AppleHDAEngineInput:1B,0,1,0:1', | ||
name: 'Built-in Microphone' | ||
}] | ||
``` | ||
### aperture bulk ### | ||
#### options | ||
If you've just cloned the project repo, you probably don't want to visit | ||
each local dependency to get `npm install` or any other setup commands running. | ||
This is easily fixed with `aperture bulk`, which runs your chosen command from | ||
each source's directory: | ||
##### fps | ||
``` bash | ||
# Install dependencies for all of the local modules | ||
# defined in "aperture.sources" | ||
aperture bulk npm install | ||
Type: `number`<br> | ||
Default: `30` | ||
# Remove the currently installed node_modules | ||
# folder for each local module. Note the use of -- | ||
# to allow for the -rf flags. | ||
aperture bulk -- rm -rf node_modules | ||
``` | ||
Number of frames per seconds. | ||
By default, each script will run whether or not the previous one executed | ||
successfully. You can change this behavior using the `--bail` flag: | ||
##### cropArea | ||
``` bash | ||
$ aperture bulk --bail -- bash -c 'echo hello && exit 1' | ||
hello | ||
Type: `Object` `string`<br> | ||
Default: `'none'` | ||
Error: Invalid exit code: 1 | ||
``` | ||
Record only an area of the screen. Accepts an object with `x`, `y`, `width`, `height` properties. | ||
### aperture install ### | ||
##### showCursor | ||
In practice, `aperture bulk npm install` works, but can take *a long time* | ||
when projects share a lot of common dependencies. The `install` command is | ||
a little smarter about this, and will install each linked module's dependencies | ||
for you in a way that minimises duplicate packages. | ||
Type: `boolean`<br> | ||
Default: `true` | ||
Essentially, aperture will build a list of the dependencies required for each | ||
project and their expected version. For each dependency: | ||
Show the cursor in the screen recording. | ||
* Check if all of the versions are compatible, and if so install them once | ||
in the root directory, alongside your linked modules. | ||
* Otherwise, install that module as normal. | ||
##### highlightClicks | ||
This can result in significant speedups (and a smaller `node_modules` folder) | ||
for installs when working on larger projects. | ||
Type: `boolean`<br> | ||
Default: `false` | ||
### aperture purge ### | ||
Highlight cursor clicks in the screen recording. | ||
The last remaining thing to do is remove any other dependencies or symlinks | ||
hidden in the tree that might conflict with your new top-level ones: | ||
##### displayId | ||
``` bash | ||
aperture purge | ||
``` | ||
Type: `string`<br> | ||
Default: `main` | ||
*Use the above command with caution!* It will `rm -rf` any conflicting | ||
packages it finds along the way, and while the effects won't leave the project | ||
directory you should make sure all your changes have been checked in properly. | ||
Display to record. | ||
After that, it should be set up, and you just need to run `aperture link` | ||
every time a new dependency has been added. | ||
##### audioSourceId | ||
### aperture expand ### | ||
Type: `Object` `string`<br> | ||
Default: `'none'` | ||
Glob patterns work well for early development, but once a project starts to | ||
solidify it can be useful to return to explicit dependencies, much like | ||
the `dependencies` field is always explicit. | ||
Audio source to include in the screen recording. Should be one of the `id`'s from `instance.getAudioSources()`. | ||
When you're ready, simply run `aperture expand` to convert your glob patterns | ||
into explicit module paths. As an example, the following: | ||
``` json | ||
{ | ||
"name": "my-app", | ||
"aperture": { | ||
"sources": ["utils-*/*"] | ||
} | ||
} | ||
``` | ||
## Why | ||
Might become something like this: | ||
`aperture` was built to fulfill the needs of [Kap](https://github.com/wulkano/kap), providing a JavaScript interface to the **best** available method for recording the screen. | ||
That's why it's currently a wrapper for a [Swift script](https://github.com/wulkano/aperture/blob/master/swift/aperture/main.swift) that records the screen using the [AVFoundation framework](https://developer.apple.com/av-foundation/). | ||
``` json | ||
{ | ||
"name": "my-app", | ||
"aperture": { | ||
"sources": [ | ||
"utils-1/module-a", | ||
"utils-1/module-c", | ||
"utils-2/module-b", | ||
"utils-2/module-d" | ||
] | ||
} | ||
} | ||
``` | ||
#### But you can use `ffmpeg -f avfoundation...` | ||
### aperture list ### | ||
Yes, we can, but the performance is terrible: | ||
You can list all of the module directories that should be linked locally using | ||
this command: | ||
#### Recording the entire screen with `ffmpeg -f avfoundation -i 1 -y test.mp4`: | ||
``` bash | ||
$ aperture list | ||
/Users/hughsk/myproject/features/config | ||
/Users/hughsk/myproject/features/credentials | ||
/Users/hughsk/myproject/features/progress | ||
/Users/hughsk/myproject/utils/ajax-data | ||
/Users/hughsk/myproject/utils/custom-element | ||
/Users/hughsk/myproject/utils/polyfill-webcomponents | ||
/Users/hughsk/myproject/utils/render-template | ||
``` | ||
 | ||
### aperture config ### | ||
#### Recording the entire screen with `aperture`: | ||
Quickly print out the project's current config using this command: | ||
 | ||
``` bash | ||
$ aperture config | ||
{ | ||
"sources": [ | ||
"utils/*", | ||
"features/*" | ||
] | ||
} | ||
``` | ||
## Linux and Windows | ||
We want to bring `aperture` to Linux and Windows, but we don't have time and resources for such tasks (we're Mac users), so **any help is more than welcome**. We just want to enforce two things: **performance** and **quality** – it doesn't matter how (`ffmpeg`, custom built native lib, etc) they are achieved. | ||
## Upcoming | ||
`aperture` is in its early days. We're working on adding more features, such as *export to GIF*, compression options, support for multiple displays, support for audio and much more. Check out our [`aperture`](https://github.com/wulkano/kap/issues?q=is%3Aissue+is%3Aopen+label%3Aaperture) issues on **Kap** to learn more. |
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
7826556
20951.58%2
-88.24%1
-75%0
-100%2
-85.71%3
-25%5
-85.71%87
-90.58%1
Infinity%1
Infinity%124
-39.22%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed