BaiduMusic / nobone

A server library tries to understand what developers really need.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

nobone

Overview

A server library tries to understand what developers really need.

The philosophy behind NoBone is providing possibilities rather than telling developers what they should do. All the default behaviors are just examples of how to use NoBone. All the APIs should dance together happily. So other than javascript, the idea should be ported to any other language easily.

NPM version Build Status Build status


Features

  • Code you program, not configure.
  • Built for performance.
  • Not only a good dev-tool, but also good at production.
  • Supports programmable plugins.
  • Cross platform.
  • Pure js, supports coffee by default.

Install

Install as an dependency:

npm install nobone

# View a better nobone documentation than Github readme.
node_modules/.bin/nobone --doc

Or you can install it globally:

npm i -g nobone

# View a better nobone documentation than Github readme.
nobone -d

FAQ

  1. How to view the documentation with TOC (table of contents) or offline?

If you have installed nobone globally, just execute nobone --doc or nobone -d. If you are on Windows or Mac, it will auto open the documentation.

If you have installed nobone with npm install nobone in current directory, execute node_modules/.bin/nobone -d.

  1. Why I can't execute the entrance file with nobone cli tool?

Don't execute nobone with a directory path when you want to start it with an entrance file.

  1. Why doesn't the auto-reaload work?

Check if the process.env.NODE_ENV is set to development.

  1. When serving jade or less, it doesn't work.

These are optinal packages, you have to install them first. For example, if you want nobone to support jade: npm install -g jade.


Quick Start

For more examples, go through the examples folder.

process.env.NODE_ENV = 'development'

nobone = require 'nobone'

port = 8219

# If you want to init without a specific module,
# for example 'db' and 'service' module, just exclude them:
# 	nobone {
# 		renderer: {}
# 	}
# By default it only loads two modules: `service` and `renderer`.
nb = nobone {
	db: { db_path: './test.db' }
	proxy: {}
	renderer: {}
	service: {}
}

# Service
nb.service.get '/', (req, res) ->
	# Renderer
	# It will auto-find the 'examples/fixtures/index.ejs', and render it to html.
	# You can also render jade, coffee, stylus, less, sass, markdown, or define custom handlers.
	# When you modify the `examples/fixtures/index.ejs`, the page will auto-reload.
	nb.renderer.render('examples/fixtures/index.html')
	.done (tpl_fn) ->
		res.send tpl_fn({ name: 'nobone' })

# Launch express.js
nb.service.listen port, ->
	# Kit
	# A smarter log helper.
	nb.kit.log 'Listen port ' + port

	# Open default browser.
	nb.kit.open 'http://127.0.0.1:' + port

# Static folder for auto-service of coffeescript and stylus, etc.
nb.service.use nb.renderer.static('examples/fixtures')

# Database
# Nobone has a build-in file database.
# Here we save 'a' as value 1.
nb.db.loaded.done ->
	nb.db.exec (jdb) ->
		jdb.doc.a = 1
		jdb.save('DB OK')
	.done (data) ->
		nb.kit.log data

# Proxy
# Proxy path to specific url.
nb.service.get '/proxy.*', (req, res) ->
	# If you visit "http://127.0.0.1:8013/proxy.js",
	# it'll return the "http://127.0.0.1:8013/main.js" from the remote server,
	# though here we just use a local server for test.
	nb.proxy.url req, res, "http://127.0.0.1:#{port}/main." + req.params[0]

close = ->
	# Release all the resources.
	nb.close().done ->
		nb.kit.log 'Peacefully closed.'

CLI

Install nobone globally: npm install -g nobone

# Help info
nobone -h

# Use it as a static file server for current directory.
# Visit 'http://127.0.0.1/nobone' to see a better nobone documentation.
nobone

# Use regex to filter the log info.
# Print out all the log if it contains '.ejs'
log_reg='.ejs' nobone

# Use custom logic to start up.
nobone app.js
watch_persistent=off nobone app.js

# Scaffolding helper
nobone bone -h

Plugin

Here I give a simple instruction. For a real example, see nobone-sync.

Package config

NoBone support a simple way to implement npm plugin. And your npm package doesn't have to waist time to install nobone dependencies. The package.json file can only have these properties:

{
  "name": "nobone-sample",
  "version": "0.0.1",
  "description": "A sample nobone plugin.",
  "main": "main.coffee"
}

The name of the plugin should prefixed with nobone-.

Main Entrance File

The main.coffee file may looks like:

{ kit } = require 'nobone'
kit.log 'sample plugin'

Use A Plugin

Suppose we have published the nobone-sampe plugin with npm.

Other people can use the plugin after installing it with either npm install nobone-sample or npm install -g nobone-sample.

To run the plugin simply use nobone sample.

You can use nobone ls to list all installed plugins.


Modules API

It's highly recommended reading the API doc locally by command nobone --doc

nobone

NoBone has several modules and a helper lib. All the modules are optional. Only the kit lib is loaded by default and is not optional.

Most of the async functions are implemented with [Promise][Promise]. [Promise]: https://github.com/petkaantonov/bluebird

Main constructor.

  • param: modules { Object }

    By default, it only load two modules, service and renderer:

    {
    	service: {}
    	renderer: {}
    	db: null
    	proxy: null
    
    	lang_dir: null # language set directory
    }
  • param: opts { Object }

    Other options.

  • return: { Object }

    A nobone instance.

Release the resources.

The NoBone client helper.

  • static:

  • param: opts { Object }

    The options of the client, defaults:

    {
    	auto_reload: kit.is_development()
    	lang_current: kit.lang_current
    	lang_data: kit.lang_data
    	host: '' # The host of the event source.
    }
  • param: use_js { Boolean }

    By default use html. Default is false.

  • return: { String }

    The code of client helper.

service

It is just a Express.js wrap.

Create a Service instance.

  • param: opts { Object }

    Defaults:

    {
    	auto_log: kit.is_development()
    	enable_remote_log: kit.is_development()
    	enable_sse: kit.is_development()
    	express: {}
    }
  • return: { Service }

The server object of the express object.

  • type: { http.Server }

    Ref

A Server-Sent Event Manager. The namespace of nobone sse is /nobone-sse. For more info see [Using server-sent events][Using server-sent events]. NoBone use it to implement the live-reload of web assets. [Using server-sent events]: https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events

  • type: { SSE }

  • property: sessions { Array }

    The sessions of connected clients.

  • property: retry { Integer }

    The reconnection time to use when attempting to send the event, unit is ms. Default is 1000ms. A session object is something like:

    {
    	req  # The express.js req object.
    	res  # The express.js res object.
    }
  • example:

    You browser code should be something like this:

    es = new EventSource('/nobone-sse')
    es.addEventListener('event_name', (e) ->
    	msg = JSON.parse(e.data)
    	console.log(msg)

This event will be triggered when a sse connection started. The event name is a combination of sse_connected and req.path, for example: "sse_connected/test"

  • event: { sse_connected }

  • param: session { SSE_session }

    The session object of current connection.

This event will be triggered when a sse connection closed.

  • event: { sse_close }

  • param: session { SSE_session }

    The session object of current connection.

Create a sse session.

  • param: req { Express.req }

  • param: res { Express.res }

  • return: { SSE_session }

Emit message to client.

  • param: event { String }

    The event name.

  • param: msg { Object | String }

    The message to send to the client.

Broadcast a event to clients.

  • param: event { String }

    The event name.

  • param: msg { Object | String }

    The data you want to emit to session.

  • param: { String }

    [path] The namespace of target sessions. If not set, broadcast to all clients.

renderer

An abstract renderer for any content, such as source code or image files. It automatically uses high performance memory cache. This renderer helps nobone to build a passive compilation architecture. You can run the benchmark to see the what differences it makes. Even for huge project the memory usage is negligible.

Create a Renderer instance.

  • param: opts { Object }

    Defaults:

    {
    	enable_watcher: kit.is_development()
    	auto_log: kit.is_development()
    
    	# If renderer detects this pattern, it will auto-inject `nobone_client.js`
    	# into the page.
    	inject_client_reg: /<html[^<>]*>[\s\S]*</html>/i
    
    	cache_dir: '.nobone/renderer_cache'
    	cache_limit: 1024
    
    	file_handlers: {
    		'.html': {
    			default: true
    			ext_src: ['.ejs', '.jade']
    			extra_watch: { path1: 'comment1', path2: 'comment2', ... } # Extra files to watch.
    			encoding: 'utf8' # optional, default is 'utf8'
    			dependency_reg: {
    				'.ejs': /<%[\n\r\s]*include\s+([^\r\n]+)\s*%>/
    				'.jade': /^\s*(?:include|extends)\s+([^\r\n]+)/
    			}
    			compiler: (str, path, data) -> ...
    		}
    
    		# Simple coffee compiler
    		'.js': {
    			ext_src: '.coffee'
    			compiler: (str, path) -> ...
    		}
    
    		# Browserify a main entrance file.
    		'.jsb': {
    			type: '.js'
    			ext_src: '.coffee'
    			dependency_reg: /require\s+([^\r\n]+)/
    			compiler: (str, path) -> ...
    		}
    		'.css': {
    			ext_src: ['.styl', '.less', '.sass', '.scss']
    			dependency_reg: {
       			'.styl': /@(?:import|require)\s+([^\r\n]+)/
    				'.less': /@import\s*(?:\(\w+\))?\s*([^\r\n]+)/
    				'.sass': /@import\s+([^\r\n]+)/
    				'.scss': /@import\s+([^\r\n]+)/
    			}
    			compiler: (str, path) -> ...
    		}
    		'.md': {
    			type: 'html' # Force type, optional.
    			ext_src: ['.md', '.markdown']
    			compiler: (str, path) -> ...
    		}
    	}
    }
  • return: { Renderer }

The compiler can handle any type of file.

  • context: { File_handler }

    Properties:

    {
    	ext: String # The current file's extension.
    	opts: Object # The current options of renderer.
    	dependency_reg: RegExp # The regex to match dependency path. Regex or Table.
    	dependency_roots: Array | String # The root directories for searching dependencies.
    
    	# The source map informantion.
    	# If you need source map support, the `source_map`property
    	# must be set during the compile process. If you use inline source map,
    	# this property shouldn't be set.
    	source_map: String or Object
    }
  • param: str { String }

    Source content.

  • param: path { String }

    For debug info.

  • param: data { Any }

    The data sent from the render function. when you call the render directly. Default is an object:

    {
    	_: lodash
    	inject_client: kit.is_development()
    }
  • return: { Promise }

    Promise that contains the compiled content.

You can access all the file_handlers here. Manipulate them at runtime.

  • type: { Object }

  • example:

    # We return js directly.
    renderer.file_handlers['.js'].compiler = (str) -> str

The cache pool of the result of file_handlers.compiler

  • type: { Object }

    Key is the file path.

Set a static directory proxy. Automatically compile, cache and serve source files for both deveopment and production.

  • param: opts { String | Object }

    If it's a string it represents the root_dir of this static directory. Defaults:

    {
    	root_dir: '.'
    
    	# Whether enable serve direcotry index.
    	index: kit.is_development()
    
    	inject_client: kit.is_development()
    
    	# Useful when mapping a normal path to a hashed file.
    	# Such as map 'lib/main.js' to 'lib/main-jk2x.js'.
    	req_path_handler: (path) ->
    		decodeURIComponent path
    }
  • return: { Middleware }

    Experss.js middleware.

Render a file. It will auto-detect the file extension and choose the right compiler to handle the content.

  • param: path { String | Object }

    The file path. The path extension should be the same with the compiled result file. If it's an object, it can contain any number of following params.

  • param: ext { String }

    Force the extension. Optional.

  • param: data { Object }

    Extra data you want to send to the compiler. Optional.

  • param: is_cache { Boolean }

    Whether to cache the result, default is true. Optional.

  • param: req_path { String }

    The http request path. Support it will make auto-reload more efficient.

  • return: { Promise }

    Contains the compiled content.

  • example:

    # The 'a.ejs' file may not exists, it will auto-compile
    # the 'a.ejs' or 'a.html' to html.
    renderer.render('a.html').done (html) -> kit.log(html)
    
    # if the content of 'a.ejs' is '<% var a = 10 %><%= a %>'
    renderer.render('a.ejs', '.html').done (html) -> html == '10'
    renderer.render('a.ejs').done (str) -> str == '<% var a = 10 %><%= a %>'

Release the resources.

Release memory cache of a file.

  • param: path { String }

  • event: { compile_error }

  • param: path { string }

    The error file.

  • param: err { Error }

    The error info.

  • event: { watch_file }

  • param: path { string }

    The path of the file.

  • param: curr { fs.Stats }

    Current state.

  • param: prev { fs.Stats }

    Previous state.

  • event: { file_deleted }

  • param: path { string }

    The path of the file.

  • event: { file_modified }

  • param: path { string }

    The path of the file.

Set handler cache.

  • param: handler { File_handler }

  • return: { Promise }

Generate a file handler.

  • param: path { String }

  • return: { File_handler }

db

See my [jdb][jdb] project. [jdb]: https://github.com/ysmood/jdb

Create a JDB instance.

  • param: opts { Object }

    Defaults:

    {
    	db_path: './nobone.db'
    }
  • return: { Jdb }

A promise object that help you to detect when the db is totally loaded.

  • type: { Promise }

proxy

For test, page injection development. A cross platform Fiddler alternative. Most time used with SwitchySharp.

  • extends: { http-proxy.ProxyServer }

Create a Proxy instance.

Use it to proxy one url to another.

  • param: req { http.IncomingMessage }

  • param: res { http.ServerResponse }

  • param: url { String }

    The target url forced to. Optional. Such as force 'http://test.com/a' to 'http://test.com/b', force 'http://test.com/a' to 'http://other.com/a', force 'http://test.com' to 'other.com'.

  • param: opts { Object }

    Other options. Default:

    {
    	bps: null # Limit the bandwidth byte per second.
    	global_bps: false # if the bps is the global bps.
    	agent: custom_http_agent
    }
  • param: err { Function }

    Custom error handler.

  • return: { Promise }

Http CONNECT method tunneling proxy helper. Most times used with https proxing.

  • param: req { http.IncomingMessage }

  • param: sock { net.Socket }

  • param: head { Buffer }

  • param: host { String }

    The host force to. It's optional.

  • param: port { Int }

    The port force to. It's optional.

  • param: err { Function }

    Custom error handler.

  • example:

    nobone = require 'nobone'
    { proxy, service } = nobone { proxy:{}, service: {} }
    
    # Directly connect to the original site.
    service.server.on 'connect', proxy.connect

A pac helper.

  • param: curr_host { String }

    The current host for proxy server. It's optional.

  • param: rule_handler { Function }

    Your custom pac rules. It gives you three helpers.

    url # The current client request url.
    host # The host name derived from the url.
    curr_host = 'PROXY host:port;' # Nobone server host address.
    direct =  "DIRECT;"
    match = (pattern) -> # A function use shExpMatch to match your url.
    proxy = (target) -> # return 'PROXY target;'.
  • return: { Function }

    Express Middleware.

kit

All the async functions in kit return promise object. Most time I use it to handle files and system staffs.

kit extends all the promise functions of [fs-more][fs-more]. [fs-more]: https://github.com/ysmood/fs-more

  • example:

    kit.readFile('test.txt').done (str) ->
    	console.log str
    
    kit.outputFile('a.txt', 'test').done()

The lodash lib.

An throttle version of Promise.all, it runs all the tasks under a concurrent limitation.

  • param: limit { Int }

    The max task to run at the same time. It's optional. Default is Infinity.

  • param: list { Array | Function }

    If the list is an array, it should be a list of functions or promises, and each function will return a promise. If the list is a function, it should be a iterator that returns a promise, when it returns undefined, the iteration ends.

  • param: save_resutls { Boolean }

    Whether to save each promise's result or not. Default is true.

  • param: progress { Function }

    If a task ends, the resolve value will be passed to this function.

  • return: { Promise }

  • example:

    urls = [
    	'http://a.com'
    	'http://b.com'
    	'http://c.com'
    	'http://d.com'
    ]
    tasks = [
    	-> kit.request url[0]
    	-> kit.request url[1]
    	-> kit.request url[2]
    	-> kit.request url[3]
    ]
    
    kit.async(tasks).then ->
    	kit.log 'all done!'
    
    kit.async(2, tasks).then ->
    	kit.log 'max concurrent limit is 2'
    
    kit.async 3, ->
    	url = urls.pop()
    	if url
    		kit.request url
    .then ->
    	kit.log 'all done!'

Creates a function that is the composition of the provided functions. Besides it can also accept async function that returns promise. It's more powerful than _.compose.

  • param: fns { Function | Array }

    Functions that return promise or any value. And the array can also contains promises.

  • return: { Function }

    A composed function that will return a promise.

  • example:

    # It helps to decouple sequential pipeline code logic.
    
    create_url = (name) ->
    	return "http://test.com/" + name
    
    curl = (url) ->
    	kit.request(url).then ->
    		kit.log 'get'
    
    save = (str) ->
    	kit.outputFile('a.txt', str).then ->
    		kit.log 'saved'
    
    download = kit.compose create_url, curl, save
    # same as "download = kit.compose [create_url, curl, save]"
    
    download 'home'

Daemonize a program.

  • param: opts { Object }

    Defaults: { bin: 'node' args: ['app.js'] stdout: 'stdout.log' stderr: 'stderr.log' }

  • return: { Porcess }

    The daemonized process.

A simple decrypt helper

  • param: data { Any }

  • param: password { String | Buffer }

  • param: algorithm { String }

    Default is 'aes128'.

  • return: { Buffer }

A simple encrypt helper

  • param: data { Any }

  • param: password { String | Buffer }

  • param: algorithm { String }

    Default is 'aes128'.

  • return: { Buffer }

A shortcut to set process option with specific mode, and keep the current env variables.

  • param: mode { String }

    'development', 'production', etc.

  • return: { Object }

    process.env object.

A log error shortcut for kit.log(msg, 'error', opts)

  • param: msg { Any }

  • param: opts { Object }

A better child_process.exec.

  • param: cmd { String }

    Shell commands.

  • param: shell { String }

    Shell name. Such as bash, zsh. Optinal.

  • return: { Promise }

    Resolves when the process's stdio is drained.

  • example:

    kit.exec """
    a=10
    echo $a
    """
    
    # Bash doesn't support "**" recusive match pattern.
    kit.exec """
    echo **/*.css
    """, 'zsh'

See my project [fs-more][fs-more]. [fs-more]: https://github.com/ysmood/fs-more

A scaffolding helper to generate template project. The lib/cli.coffee used it as an example.

  • param: opts { Object }

    Defaults:

    {
    	src_dir: null
    	patterns: '**'
    	dest_dir: null
    	data: {}
    	compile: (str, data, path) ->
    		compile str
    }
  • return: { Promise }

See the https://github.com/isaacs/node-glob

  • param: patterns { String | Array }

    Minimatch pattern.

  • param: opts { Object }

    The glob options.

  • return: { Promise }

    Contains the path list.

See my [jhash][jhash] project. [jhash]: https://github.com/ysmood/jhash

It will find the right key/value pair in your defined kit.lang_set. If it cannot find the one, it will output the key directly.

  • param: cmd { String }

    The original text.

  • param: name { String }

    The target language name.

  • param: lang_set { String }

    Specific a language collection.

  • return: { String }

  • example:

    lang_set =
    	cn:
    		China: '**'
    		open:
    			formal: '开启' # Formal way to say 'open'.
    			casual: '打开' # Casual way to say 'open'.
    	jp:
    		human: '人間'
    		'find %s men': '%sっ人が見付かる'
    
    kit.lang('China', 'cn', lang_set) # -> '**'
    kit.lang('open|casual', 'cn', lang_set) # -> '打开'
    kit.lang('find %s men', [10], 'jp', lang_set) # -> '10っ人が見付かる'
  • example:

    Supports we have two json file in langs_dir_path folder.

    • cn.js, content: module.exports = { China: '**' }
    • jp.coffee, content: module.exports = 'Good weather.': '日和。'
    kit.lang_load 'langs_dir_path'
    
    kit.lang_current = 'cn'
    'China'.l # '**'
    'Good weather.'.lang('jp') # '日和。'
    
    kit.lang_current = 'en'
    'China'.l # 'China'
    'Good weather.'.lang('jp') # 'Good weather.'

Language collections.

  • type: { Object }

  • example:

    kit.lang_set = {
    	'cn': { 'China': '**' }
    }

Current default language.

Load language set directory and save them into the kit.lang_set.

  • param: dir_path { String }

    The directory path that contains js or coffee files.

  • example:

    kit.lang_load 'assets/lang'
    kit.lang_current = 'cn'
    kit.log 'test'.l # -> '测试'.
    kit.log '%s persons'.lang([10]) # -> '10 persons'

For debugging use. Dump a colorful object.

  • param: obj { Object }

    Your target object.

  • param: opts { Object }

    Options. Default: { colors: true, depth: 5 }

  • return: { String }

Nobone use it to check the running mode of the app. Overwrite it if you want to control the check logic. By default it returns the rocess.env.NODE_ENV == 'development'.

Nobone use it to check the running mode of the app. Overwrite it if you want to control the check logic. By default it returns the rocess.env.NODE_ENV == 'production'.

  • return: { Boolean }

A better log for debugging, it uses the kit.inspect to log.

You can use terminal command like log_reg='pattern' node app.js to filter the log info.

You can use log_trace='on' node app.js to force each log end with a stack trace.

  • param: msg { Any }

    Your log message.

  • param: action { String }

    'log', 'error', 'warn'.

  • param: opts { Object }

    Default is same with kit.inspect

Monitor an application and automatically restart it when file changed. When the monitored app exit with error, the monitor itself will also exit. It will make sure your app crash properly.

  • param: opts { Object }

    Defaults:

    {
    	bin: 'node'
    	args: ['app.js']
    	watch_list: ['app.js']
    	mode: 'development'
    }
  • return: { Process }

    The child process.

Node version. Such as v0.10.23 is 0.1023, v0.10.1 is 0.1001.

  • type: { Float }

Open a thing that your system can recognize. Now only support Windows, OSX or system that installed 'xdg-open'.

  • param: cmd { String }

    The thing you want to open.

  • param: opts { Object }

    The options of the node native child_process.exec.

  • return: { Promise }

    When the child process exits.

  • example:

    # Open a webpage with the default browser.
    kit.open 'http://ysmood.org'

String padding helper.

  • param: str { Sting | Number }

  • param: width { Number }

  • param: char { String }

    Padding char. Default is '0'.

  • return: { String }

  • example:

    kit.pad '1', 3 # '001'

A comments parser for coffee-script. Used to generate documentation automatically. It will traverse through all the comments.

  • param: module_name { String }

    The name of the module it belongs to.

  • param: code { String }

    Coffee source code.

  • param: path { String }

    The path of the source code.

  • param: opts { Object }

    Parser options:

    {
    	comment_reg: RegExp
    	split_reg: RegExp
    	tag_name_reg: RegExp
    	type_reg: RegExp
    	name_reg: RegExp
    	name_tags: ['param', 'property']
    	description_reg: RegExp
    }
  • return: { Array }

    The parsed comments. Each item is something like:

    {
    	module: 'nobone'
    	name: 'parse_comment'
    	description: 'A comments parser for coffee-script.'
    	tags: [
    		{
    			tag_name: 'param'
    			type: 'string'
    			name: 'code'
    			description: 'The name of the module it belongs to.'
    			path: 'http://the_path_of_source_code'
    			index: 256 # The target char index in the file.
    			line: 32 # The line number of the target in the file.
    		}
    	]
    }

Node native module

Block terminal and wait for user inputs. Useful when you need in-terminal user interaction.

The promise lib.

Much much faster than the native require of node, but you should follow some rules to use it safely.

  • param: module_name { String }

    Moudle path is not allowed!

  • param: done { Function }

    Run only the first time after the module loaded.

  • return: { Module }

    The module that you require.

A powerful extended combination of http.request and https.request.

  • param: opts { Object }

    The same as the [http.request][http.request], but with some extra options:

    {
    	url: 'It is not optional, String or Url Object.'
    	body: true # Other than return `res` with `res.body`, return `body` directly.
    	redirect: 0 # Max times of auto redirect. If 0, no auto redirect.
    
    	# Set null to use buffer, optional.
    	# It supports GBK, Shift_JIS etc.
    	# For more info, see https://github.com/ashtuchkin/iconv-lite
    	res_encoding: 'auto'
    
    	# It's string, object or buffer, optional. When it's an object,
    	# The request will be 'application/x-www-form-urlencoded'.
    	req_data: null
    
    	auto_end_req: true # auto end the request.
    	req_pipe: Readable Stream.
    	res_pipe: Writable Stream.
    }

    And if set opts as string, it will be treated as the url. [http.request]: http://nodejs.org/api/http.html#http_http_request_options_callback

  • return: { Promise }

    Contains the http response object, it has an extra body property. You can also get the request object by using Promise.req, for example:

    p = kit.request 'http://test.com'
    p.req.on 'response', (res) ->
    	kit.log res.headers['content-length']
    p.done (body) ->
    	kit.log body # html or buffer
    
    kit.request {
    	url: 'https://test.com'
    	body: false
    }
    .done (res) ->
    	kit.log res.body
    	kit.log res.headers

A safer version of child_process.spawn to run a process on Windows or Linux. It will automatically add node_modules/.bin to the PATH environment variable.

  • param: cmd { String }

    Path of an executable program.

  • param: args { Array }

    CLI arguments.

  • param: opts { Object }

    Process options. Same with the Node.js official doc. Default will inherit the parent's stdio.

  • return: { Promise }

    The promise.process is the child process object. When the child process ends, it will resolve.

Node native module

Watch a file. If the file changes, the handler will be invoked. You can change the polling interval by using process.env.polling_watch. Use process.env.watch_persistent = 'off' to disable the persistent. For samba server, we have to choose watchFile than watch. variable.

  • param: path { String }

    The file path

  • param: handler { Function }

    Event listener. The handler has these params:

    • file path
    • current fs.Stats
    • previous fs.Stats
    • if its a deletion
  • param: auto_unwatch { Boolean }

    Auto unwatch the file while file deletion. Default is true.

  • return: { Function }

    The wrapped watch listeners.

  • example:

    process.env.watch_persistent = 'off'
    kit.watch_file 'a.js', (path, curr, prev, is_deletion) ->
    	if curr.mtime != prev.mtime
    		kit.log path

Watch files, when file changes, the handler will be invoked. It takes the advantage of kit.watch_file.

  • param: patterns { Array }

    String array with minimatch syntax. Such as ['*/**.css', 'lib/**/*.js'].

  • param: handler { Function }

  • return: { Promise }

    It contains the wrapped watch listeners.

  • example:

    kit.watch_files '*.js', (path, curr, prev, is_deletion) ->
    	kit.log path

Watch directory and all the files in it. It supports three types of change: create, modify, move, delete.

  • param: opts { Object }

    Defaults:

    {
    	dir: '.'
    	pattern: '**' # minimatch, string or array
    
    	# Whether to watch POSIX hidden file.
    	dot: false
    
    	# If the "path" ends with '/' it's a directory, else a file.
    	handler: (type, path, old_path) ->
    }
  • return: { Promise }

  • example:

    # Only current folder, and only watch js and css file.
    kit.watch_dir {
    	dir: 'lib'
    	pattern: '*.+(js|css)'
    	handler: (type, path) ->
    		kit.log type
    		kit.log path
    	watched_list: {} # If you use watch_dir recursively, you need a global watched_list
    }

Changelog

See the doc/changelog.md file.


Unit Test

npm test


Benchmark

Memory vs Stream

Memory cache is faster than direct file streaming even on SSD machine. It's hard to test the real condition, because most of the file system will cache a file into memory if it being read lot of times.
Type Performance
memory 1,225 ops/sec ±3.42% (74 runs sampled)
stream 933 ops/sec ±3.23% (71 runs sampled)

crc32 vs jhash

As we can see, jhash is about 1.5x faster than crc32. Their results of collision test are nearly the same.
Type Performance
crc buffer 5,903 ops/sec ±0.52% (100 runs sampled)
crc str 54,045 ops/sec ±6.67% (83 runs sampled)
jhash buffer 9,756 ops/sec ±0.67% (101 runs sampled)
jhash str 72,056 ops/sec ±0.36% (94 runs sampled)
Type Time Collision
jhash 10.002s 0.004007480630510286% (15 / 374300)
crc32 10.001s 0.004445855827246745% (14 / 314900)

Road Map

Decouple libs.

Better test coverage.


Lisence

BSD

May 2014, Yad Smood

About

A server library tries to understand what developers really need.