Mitch Garnaat

Adding an initial S3 sample and code to register for event notification on an S3 bucket.

Showing 56 changed files with 5094 additions and 2 deletions
...@@ -170,7 +170,20 @@ class Kappa(object): ...@@ -170,7 +170,20 @@ class Kappa(object):
170 for log_event in response['events']: 170 for log_event in response['events']:
171 print(log_event['message']) 171 print(log_event['message'])
172 172
173 - def add_event_source(self): 173 + def _get_function_arn(self):
174 + name = self.config['lambda']['name']
175 + arn = None
176 + lambda_svc = self.session.create_client('lambda', self.region)
177 + try:
178 + response = lambda_svc.get_function_configuration(
179 + FunctionName=name)
180 + LOG.debug(response)
181 + arn = response['FunctionARN']
182 + except Exception:
183 + LOG.debug('Unable to find ARN for function: %s' % name)
184 + return arn
185 +
186 + def _add_kinesis_event_source(self, event_source_arn):
174 lambda_svc = self.session.create_client('lambda', self.region) 187 lambda_svc = self.session.create_client('lambda', self.region)
175 try: 188 try:
176 invoke_role = self.get_role_arn( 189 invoke_role = self.get_role_arn(
...@@ -178,12 +191,38 @@ class Kappa(object): ...@@ -178,12 +191,38 @@ class Kappa(object):
178 response = lambda_svc.add_event_source( 191 response = lambda_svc.add_event_source(
179 FunctionName=self.config['lambda']['name'], 192 FunctionName=self.config['lambda']['name'],
180 Role=invoke_role, 193 Role=invoke_role,
181 - EventSource=self.config['lambda']['event_source'], 194 + EventSource=event_source_arn,
182 BatchSize=self.config['lambda'].get('batch_size', 100)) 195 BatchSize=self.config['lambda'].get('batch_size', 100))
183 LOG.debug(response) 196 LOG.debug(response)
184 except Exception: 197 except Exception:
185 LOG.exception('Unable to add event source') 198 LOG.exception('Unable to add event source')
186 199
200 + def _add_s3_event_source(self, event_source_arn):
201 + s3_svc = self.session.create_client('s3', self.region)
202 + bucket_name = event_source_arn.split(':')[-1]
203 + invoke_role = self.get_role_arn(
204 + self.config['cloudformation']['invoke_role'])
205 + notification_spec = {
206 + 'CloudFunctionConfiguration': {
207 + 'Id': 'Kappa-%s-notification' % self.config['lambda']['name'],
208 + 'Events': [e for e in self.config['lambda']['s3_events']],
209 + 'CloudFunction': self._get_function_arn(),
210 + 'InvocationRole': invoke_role}}
211 + response = s3_svc.put_bucket_notification(
212 + Bucket=bucket_name,
213 + NotificationConfiguration=notification_spec)
214 + LOG.debug(response)
215 +
216 + def add_event_source(self):
217 + event_source_arn = self.config['lambda']['event_source']
218 + _, _, svc, _ = event_source_arn.split(':', 3)
219 + if svc == 'kinesis':
220 + self._add_kinesis_event_source(event_source_arn)
221 + elif svc == 's3':
222 + self._add_s3_event_source(event_source_arn)
223 + else:
224 + raise ValueError('Unsupported event source: %s' % event_source_arn)
225 +
187 def deploy(self): 226 def deploy(self):
188 self.create_update_roles( 227 self.create_update_roles(
189 self.config['cloudformation']['stack_name'], 228 self.config['cloudformation']['stack_name'],
......
1 +---
2 +profile: personal
3 +region: us-east-1
4 +cloudformation:
5 + template: roles.cf
6 + stack_name: TestS3
7 + exec_role: ExecRole
8 + invoke_role: InvokeRole
9 +lambda:
10 + name: S3Sample
11 + zipfile_name: S3Sample.zip
12 + description: Testing S3 Lambda handler
13 + path: examplefolder
14 + handler: CreateThumbnail.handler
15 + runtime: nodejs
16 + memory_size: 128
17 + timeout: 3
18 + mode: event
19 + test_data: input.json
20 + event_source: arn:aws:s3:::garnaat_pub
21 + s3_events:
22 + - s3:ObjectCreated:*
...\ No newline at end of file ...\ No newline at end of file
1 +// dependencies
2 +var async = require('async');
3 +var AWS = require('aws-sdk');
4 +var gm = require('gm')
5 + .subClass({ imageMagick: true }); // Enable ImageMagick integration.
6 +var util = require('util');
7 +
8 +// constants
9 +var MAX_WIDTH = 100;
10 +var MAX_HEIGHT = 100;
11 +
12 +// get reference to S3 client
13 +var s3 = new AWS.S3();
14 +
15 +exports.handler = function(event, context) {
16 + // Read options from the event.
17 + console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));
18 + var srcBucket = event.Records[0].s3.bucket.name;
19 + var srcKey = event.Records[0].s3.object.key;
20 + var dstBucket = srcBucket + "resized";
21 + var dstKey = "resized-" + srcKey;
22 +
23 + // Sanity check: validate that source and destination are different buckets.
24 + if (srcBucket == dstBucket) {
25 + console.error("Destination bucket must not match source bucket.");
26 + return;
27 + }
28 +
29 + // Infer the image type.
30 + var typeMatch = srcKey.match(/\.([^.]*)$/);
31 + if (!typeMatch) {
32 + console.error('unable to infer image type for key ' + srcKey);
33 + return;
34 + }
35 + var imageType = typeMatch[1];
36 + if (imageType != "jpg" && imageType != "png") {
37 + console.log('skipping non-image ' + srcKey);
38 + return;
39 + }
40 +
41 + // Download the image from S3, transform, and upload to a different S3 bucket.
42 + async.waterfall([
43 + function download(next) {
44 + // Download the image from S3 into a buffer.
45 + s3.getObject({
46 + Bucket: srcBucket,
47 + Key: srcKey
48 + },
49 + next);
50 + },
51 + function tranform(response, next) {
52 + gm(response.Body).size(function(err, size) {
53 + // Infer the scaling factor to avoid stretching the image unnaturally.
54 + var scalingFactor = Math.min(
55 + MAX_WIDTH / size.width,
56 + MAX_HEIGHT / size.height
57 + );
58 + var width = scalingFactor * size.width;
59 + var height = scalingFactor * size.height;
60 +
61 + // Transform the image buffer in memory.
62 + this.resize(width, height)
63 + .toBuffer(imageType, function(err, buffer) {
64 + if (err) {
65 + next(err);
66 + } else {
67 + next(null, response.ContentType, buffer);
68 + }
69 + });
70 + });
71 + },
72 + function upload(contentType, data, next) {
73 + // Stream the transformed image to a different S3 bucket.
74 + s3.putObject({
75 + Bucket: dstBucket,
76 + Key: dstKey,
77 + Body: data,
78 + ContentType: contentType
79 + },
80 + next);
81 + }
82 + ], function (err) {
83 + if (err) {
84 + console.error(
85 + 'Unable to resize ' + srcBucket + '/' + srcKey +
86 + ' and upload to ' + dstBucket + '/' + dstKey +
87 + ' due to an error: ' + err
88 + );
89 + } else {
90 + console.log(
91 + 'Successfully resized ' + srcBucket + '/' + srcKey +
92 + ' and uploaded to ' + dstBucket + '/' + dstKey
93 + );
94 + }
95 +
96 + context.done();
97 + }
98 + );
99 +};
1 +language: node_js
2 +node_js:
3 + - "0.10"
1 +Copyright (c) 2010-2014 Caolan McMahon
2 +
3 +Permission is hereby granted, free of charge, to any person obtaining a copy
4 +of this software and associated documentation files (the "Software"), to deal
5 +in the Software without restriction, including without limitation the rights
6 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 +copies of the Software, and to permit persons to whom the Software is
8 +furnished to do so, subject to the following conditions:
9 +
10 +The above copyright notice and this permission notice shall be included in
11 +all copies or substantial portions of the Software.
12 +
13 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 +THE SOFTWARE.
1 +# Async.js
2 +
3 +[![Build Status via Travis CI](https://travis-ci.org/caolan/async.svg?branch=master)](https://travis-ci.org/caolan/async)
4 +
5 +
6 +Async is a utility module which provides straight-forward, powerful functions
7 +for working with asynchronous JavaScript. Although originally designed for
8 +use with [Node.js](http://nodejs.org), it can also be used directly in the
9 +browser. Also supports [component](https://github.com/component/component).
10 +
11 +Async provides around 20 functions that include the usual 'functional'
12 +suspects (`map`, `reduce`, `filter`, `each`…) as well as some common patterns
13 +for asynchronous control flow (`parallel`, `series`, `waterfall`…). All these
14 +functions assume you follow the Node.js convention of providing a single
15 +callback as the last argument of your `async` function.
16 +
17 +
18 +## Quick Examples
19 +
20 +```javascript
21 +async.map(['file1','file2','file3'], fs.stat, function(err, results){
22 + // results is now an array of stats for each file
23 +});
24 +
25 +async.filter(['file1','file2','file3'], fs.exists, function(results){
26 + // results now equals an array of the existing files
27 +});
28 +
29 +async.parallel([
30 + function(){ ... },
31 + function(){ ... }
32 +], callback);
33 +
34 +async.series([
35 + function(){ ... },
36 + function(){ ... }
37 +]);
38 +```
39 +
40 +There are many more functions available so take a look at the docs below for a
41 +full list. This module aims to be comprehensive, so if you feel anything is
42 +missing please create a GitHub issue for it.
43 +
44 +## Common Pitfalls
45 +
46 +### Binding a context to an iterator
47 +
48 +This section is really about `bind`, not about `async`. If you are wondering how to
49 +make `async` execute your iterators in a given context, or are confused as to why
50 +a method of another library isn't working as an iterator, study this example:
51 +
52 +```js
53 +// Here is a simple object with an (unnecessarily roundabout) squaring method
54 +var AsyncSquaringLibrary = {
55 + squareExponent: 2,
56 + square: function(number, callback){
57 + var result = Math.pow(number, this.squareExponent);
58 + setTimeout(function(){
59 + callback(null, result);
60 + }, 200);
61 + }
62 +};
63 +
64 +async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){
65 + // result is [NaN, NaN, NaN]
66 + // This fails because the `this.squareExponent` expression in the square
67 + // function is not evaluated in the context of AsyncSquaringLibrary, and is
68 + // therefore undefined.
69 +});
70 +
71 +async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){
72 + // result is [1, 4, 9]
73 + // With the help of bind we can attach a context to the iterator before
74 + // passing it to async. Now the square function will be executed in its
75 + // 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`
76 + // will be as expected.
77 +});
78 +```
79 +
80 +## Download
81 +
82 +The source is available for download from
83 +[GitHub](http://github.com/caolan/async).
84 +Alternatively, you can install using Node Package Manager (`npm`):
85 +
86 + npm install async
87 +
88 +__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed
89 +
90 +## In the Browser
91 +
92 +So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5.
93 +
94 +Usage:
95 +
96 +```html
97 +<script type="text/javascript" src="async.js"></script>
98 +<script type="text/javascript">
99 +
100 + async.map(data, asyncProcess, function(err, results){
101 + alert(results);
102 + });
103 +
104 +</script>
105 +```
106 +
107 +## Documentation
108 +
109 +### Collections
110 +
111 +* [`each`](#each)
112 +* [`eachSeries`](#eachSeries)
113 +* [`eachLimit`](#eachLimit)
114 +* [`map`](#map)
115 +* [`mapSeries`](#mapSeries)
116 +* [`mapLimit`](#mapLimit)
117 +* [`filter`](#filter)
118 +* [`filterSeries`](#filterSeries)
119 +* [`reject`](#reject)
120 +* [`rejectSeries`](#rejectSeries)
121 +* [`reduce`](#reduce)
122 +* [`reduceRight`](#reduceRight)
123 +* [`detect`](#detect)
124 +* [`detectSeries`](#detectSeries)
125 +* [`sortBy`](#sortBy)
126 +* [`some`](#some)
127 +* [`every`](#every)
128 +* [`concat`](#concat)
129 +* [`concatSeries`](#concatSeries)
130 +
131 +### Control Flow
132 +
133 +* [`series`](#seriestasks-callback)
134 +* [`parallel`](#parallel)
135 +* [`parallelLimit`](#parallellimittasks-limit-callback)
136 +* [`whilst`](#whilst)
137 +* [`doWhilst`](#doWhilst)
138 +* [`until`](#until)
139 +* [`doUntil`](#doUntil)
140 +* [`forever`](#forever)
141 +* [`waterfall`](#waterfall)
142 +* [`compose`](#compose)
143 +* [`seq`](#seq)
144 +* [`applyEach`](#applyEach)
145 +* [`applyEachSeries`](#applyEachSeries)
146 +* [`queue`](#queue)
147 +* [`priorityQueue`](#priorityQueue)
148 +* [`cargo`](#cargo)
149 +* [`auto`](#auto)
150 +* [`retry`](#retry)
151 +* [`iterator`](#iterator)
152 +* [`apply`](#apply)
153 +* [`nextTick`](#nextTick)
154 +* [`times`](#times)
155 +* [`timesSeries`](#timesSeries)
156 +
157 +### Utils
158 +
159 +* [`memoize`](#memoize)
160 +* [`unmemoize`](#unmemoize)
161 +* [`log`](#log)
162 +* [`dir`](#dir)
163 +* [`noConflict`](#noConflict)
164 +
165 +
166 +## Collections
167 +
168 +<a name="forEach" />
169 +<a name="each" />
170 +### each(arr, iterator, callback)
171 +
172 +Applies the function `iterator` to each item in `arr`, in parallel.
173 +The `iterator` is called with an item from the list, and a callback for when it
174 +has finished. If the `iterator` passes an error to its `callback`, the main
175 +`callback` (for the `each` function) is immediately called with the error.
176 +
177 +Note, that since this function applies `iterator` to each item in parallel,
178 +there is no guarantee that the iterator functions will complete in order.
179 +
180 +__Arguments__
181 +
182 +* `arr` - An array to iterate over.
183 +* `iterator(item, callback)` - A function to apply to each item in `arr`.
184 + The iterator is passed a `callback(err)` which must be called once it has
185 + completed. If no error has occured, the `callback` should be run without
186 + arguments or with an explicit `null` argument.
187 +* `callback(err)` - A callback which is called when all `iterator` functions
188 + have finished, or an error occurs.
189 +
190 +__Examples__
191 +
192 +
193 +```js
194 +// assuming openFiles is an array of file names and saveFile is a function
195 +// to save the modified contents of that file:
196 +
197 +async.each(openFiles, saveFile, function(err){
198 + // if any of the saves produced an error, err would equal that error
199 +});
200 +```
201 +
202 +```js
203 +// assuming openFiles is an array of file names
204 +
205 +async.each(openFiles, function( file, callback) {
206 +
207 + // Perform operation on file here.
208 + console.log('Processing file ' + file);
209 +
210 + if( file.length > 32 ) {
211 + console.log('This file name is too long');
212 + callback('File name too long');
213 + } else {
214 + // Do work to process file here
215 + console.log('File processed');
216 + callback();
217 + }
218 +}, function(err){
219 + // if any of the file processing produced an error, err would equal that error
220 + if( err ) {
221 + // One of the iterations produced an error.
222 + // All processing will now stop.
223 + console.log('A file failed to process');
224 + } else {
225 + console.log('All files have been processed successfully');
226 + }
227 +});
228 +```
229 +
230 +---------------------------------------
231 +
232 +<a name="forEachSeries" />
233 +<a name="eachSeries" />
234 +### eachSeries(arr, iterator, callback)
235 +
236 +The same as [`each`](#each), only `iterator` is applied to each item in `arr` in
237 +series. The next `iterator` is only called once the current one has completed.
238 +This means the `iterator` functions will complete in order.
239 +
240 +
241 +---------------------------------------
242 +
243 +<a name="forEachLimit" />
244 +<a name="eachLimit" />
245 +### eachLimit(arr, limit, iterator, callback)
246 +
247 +The same as [`each`](#each), only no more than `limit` `iterator`s will be simultaneously
248 +running at any time.
249 +
250 +Note that the items in `arr` are not processed in batches, so there is no guarantee that
251 +the first `limit` `iterator` functions will complete before any others are started.
252 +
253 +__Arguments__
254 +
255 +* `arr` - An array to iterate over.
256 +* `limit` - The maximum number of `iterator`s to run at any time.
257 +* `iterator(item, callback)` - A function to apply to each item in `arr`.
258 + The iterator is passed a `callback(err)` which must be called once it has
259 + completed. If no error has occured, the callback should be run without
260 + arguments or with an explicit `null` argument.
261 +* `callback(err)` - A callback which is called when all `iterator` functions
262 + have finished, or an error occurs.
263 +
264 +__Example__
265 +
266 +```js
267 +// Assume documents is an array of JSON objects and requestApi is a
268 +// function that interacts with a rate-limited REST api.
269 +
270 +async.eachLimit(documents, 20, requestApi, function(err){
271 + // if any of the saves produced an error, err would equal that error
272 +});
273 +```
274 +
275 +---------------------------------------
276 +
277 +<a name="map" />
278 +### map(arr, iterator, callback)
279 +
280 +Produces a new array of values by mapping each value in `arr` through
281 +the `iterator` function. The `iterator` is called with an item from `arr` and a
282 +callback for when it has finished processing. Each of these callback takes 2 arguments:
283 +an `error`, and the transformed item from `arr`. If `iterator` passes an error to this
284 +callback, the main `callback` (for the `map` function) is immediately called with the error.
285 +
286 +Note, that since this function applies the `iterator` to each item in parallel,
287 +there is no guarantee that the `iterator` functions will complete in order.
288 +However, the results array will be in the same order as the original `arr`.
289 +
290 +__Arguments__
291 +
292 +* `arr` - An array to iterate over.
293 +* `iterator(item, callback)` - A function to apply to each item in `arr`.
294 + The iterator is passed a `callback(err, transformed)` which must be called once
295 + it has completed with an error (which can be `null`) and a transformed item.
296 +* `callback(err, results)` - A callback which is called when all `iterator`
297 + functions have finished, or an error occurs. Results is an array of the
298 + transformed items from the `arr`.
299 +
300 +__Example__
301 +
302 +```js
303 +async.map(['file1','file2','file3'], fs.stat, function(err, results){
304 + // results is now an array of stats for each file
305 +});
306 +```
307 +
308 +---------------------------------------
309 +
310 +<a name="mapSeries" />
311 +### mapSeries(arr, iterator, callback)
312 +
313 +The same as [`map`](#map), only the `iterator` is applied to each item in `arr` in
314 +series. The next `iterator` is only called once the current one has completed.
315 +The results array will be in the same order as the original.
316 +
317 +
318 +---------------------------------------
319 +
320 +<a name="mapLimit" />
321 +### mapLimit(arr, limit, iterator, callback)
322 +
323 +The same as [`map`](#map), only no more than `limit` `iterator`s will be simultaneously
324 +running at any time.
325 +
326 +Note that the items are not processed in batches, so there is no guarantee that
327 +the first `limit` `iterator` functions will complete before any others are started.
328 +
329 +__Arguments__
330 +
331 +* `arr` - An array to iterate over.
332 +* `limit` - The maximum number of `iterator`s to run at any time.
333 +* `iterator(item, callback)` - A function to apply to each item in `arr`.
334 + The iterator is passed a `callback(err, transformed)` which must be called once
335 + it has completed with an error (which can be `null`) and a transformed item.
336 +* `callback(err, results)` - A callback which is called when all `iterator`
337 + calls have finished, or an error occurs. The result is an array of the
338 + transformed items from the original `arr`.
339 +
340 +__Example__
341 +
342 +```js
343 +async.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
344 + // results is now an array of stats for each file
345 +});
346 +```
347 +
348 +---------------------------------------
349 +
350 +<a name="select" />
351 +<a name="filter" />
352 +### filter(arr, iterator, callback)
353 +
354 +__Alias:__ `select`
355 +
356 +Returns a new array of all the values in `arr` which pass an async truth test.
357 +_The callback for each `iterator` call only accepts a single argument of `true` or
358 +`false`; it does not accept an error argument first!_ This is in-line with the
359 +way node libraries work with truth tests like `fs.exists`. This operation is
360 +performed in parallel, but the results array will be in the same order as the
361 +original.
362 +
363 +__Arguments__
364 +
365 +* `arr` - An array to iterate over.
366 +* `iterator(item, callback)` - A truth test to apply to each item in `arr`.
367 + The `iterator` is passed a `callback(truthValue)`, which must be called with a
368 + boolean argument once it has completed.
369 +* `callback(results)` - A callback which is called after all the `iterator`
370 + functions have finished.
371 +
372 +__Example__
373 +
374 +```js
375 +async.filter(['file1','file2','file3'], fs.exists, function(results){
376 + // results now equals an array of the existing files
377 +});
378 +```
379 +
380 +---------------------------------------
381 +
382 +<a name="selectSeries" />
383 +<a name="filterSeries" />
384 +### filterSeries(arr, iterator, callback)
385 +
386 +__Alias:__ `selectSeries`
387 +
388 +The same as [`filter`](#filter) only the `iterator` is applied to each item in `arr` in
389 +series. The next `iterator` is only called once the current one has completed.
390 +The results array will be in the same order as the original.
391 +
392 +---------------------------------------
393 +
394 +<a name="reject" />
395 +### reject(arr, iterator, callback)
396 +
397 +The opposite of [`filter`](#filter). Removes values that pass an `async` truth test.
398 +
399 +---------------------------------------
400 +
401 +<a name="rejectSeries" />
402 +### rejectSeries(arr, iterator, callback)
403 +
404 +The same as [`reject`](#reject), only the `iterator` is applied to each item in `arr`
405 +in series.
406 +
407 +
408 +---------------------------------------
409 +
410 +<a name="reduce" />
411 +### reduce(arr, memo, iterator, callback)
412 +
413 +__Aliases:__ `inject`, `foldl`
414 +
415 +Reduces `arr` into a single value using an async `iterator` to return
416 +each successive step. `memo` is the initial state of the reduction.
417 +This function only operates in series.
418 +
419 +For performance reasons, it may make sense to split a call to this function into
420 +a parallel map, and then use the normal `Array.prototype.reduce` on the results.
421 +This function is for situations where each step in the reduction needs to be async;
422 +if you can get the data before reducing it, then it's probably a good idea to do so.
423 +
424 +__Arguments__
425 +
426 +* `arr` - An array to iterate over.
427 +* `memo` - The initial state of the reduction.
428 +* `iterator(memo, item, callback)` - A function applied to each item in the
429 + array to produce the next step in the reduction. The `iterator` is passed a
430 + `callback(err, reduction)` which accepts an optional error as its first
431 + argument, and the state of the reduction as the second. If an error is
432 + passed to the callback, the reduction is stopped and the main `callback` is
433 + immediately called with the error.
434 +* `callback(err, result)` - A callback which is called after all the `iterator`
435 + functions have finished. Result is the reduced value.
436 +
437 +__Example__
438 +
439 +```js
440 +async.reduce([1,2,3], 0, function(memo, item, callback){
441 + // pointless async:
442 + process.nextTick(function(){
443 + callback(null, memo + item)
444 + });
445 +}, function(err, result){
446 + // result is now equal to the last value of memo, which is 6
447 +});
448 +```
449 +
450 +---------------------------------------
451 +
452 +<a name="reduceRight" />
453 +### reduceRight(arr, memo, iterator, callback)
454 +
455 +__Alias:__ `foldr`
456 +
457 +Same as [`reduce`](#reduce), only operates on `arr` in reverse order.
458 +
459 +
460 +---------------------------------------
461 +
462 +<a name="detect" />
463 +### detect(arr, iterator, callback)
464 +
465 +Returns the first value in `arr` that passes an async truth test. The
466 +`iterator` is applied in parallel, meaning the first iterator to return `true` will
467 +fire the detect `callback` with that result. That means the result might not be
468 +the first item in the original `arr` (in terms of order) that passes the test.
469 +
470 +If order within the original `arr` is important, then look at [`detectSeries`](#detectSeries).
471 +
472 +__Arguments__
473 +
474 +* `arr` - An array to iterate over.
475 +* `iterator(item, callback)` - A truth test to apply to each item in `arr`.
476 + The iterator is passed a `callback(truthValue)` which must be called with a
477 + boolean argument once it has completed.
478 +* `callback(result)` - A callback which is called as soon as any iterator returns
479 + `true`, or after all the `iterator` functions have finished. Result will be
480 + the first item in the array that passes the truth test (iterator) or the
481 + value `undefined` if none passed.
482 +
483 +__Example__
484 +
485 +```js
486 +async.detect(['file1','file2','file3'], fs.exists, function(result){
487 + // result now equals the first file in the list that exists
488 +});
489 +```
490 +
491 +---------------------------------------
492 +
493 +<a name="detectSeries" />
494 +### detectSeries(arr, iterator, callback)
495 +
496 +The same as [`detect`](#detect), only the `iterator` is applied to each item in `arr`
497 +in series. This means the result is always the first in the original `arr` (in
498 +terms of array order) that passes the truth test.
499 +
500 +
501 +---------------------------------------
502 +
503 +<a name="sortBy" />
504 +### sortBy(arr, iterator, callback)
505 +
506 +Sorts a list by the results of running each `arr` value through an async `iterator`.
507 +
508 +__Arguments__
509 +
510 +* `arr` - An array to iterate over.
511 +* `iterator(item, callback)` - A function to apply to each item in `arr`.
512 + The iterator is passed a `callback(err, sortValue)` which must be called once it
513 + has completed with an error (which can be `null`) and a value to use as the sort
514 + criteria.
515 +* `callback(err, results)` - A callback which is called after all the `iterator`
516 + functions have finished, or an error occurs. Results is the items from
517 + the original `arr` sorted by the values returned by the `iterator` calls.
518 +
519 +__Example__
520 +
521 +```js
522 +async.sortBy(['file1','file2','file3'], function(file, callback){
523 + fs.stat(file, function(err, stats){
524 + callback(err, stats.mtime);
525 + });
526 +}, function(err, results){
527 + // results is now the original array of files sorted by
528 + // modified date
529 +});
530 +```
531 +
532 +__Sort Order__
533 +
534 +By modifying the callback parameter the sorting order can be influenced:
535 +
536 +```js
537 +//ascending order
538 +async.sortBy([1,9,3,5], function(x, callback){
539 + callback(err, x);
540 +}, function(err,result){
541 + //result callback
542 +} );
543 +
544 +//descending order
545 +async.sortBy([1,9,3,5], function(x, callback){
546 + callback(err, x*-1); //<- x*-1 instead of x, turns the order around
547 +}, function(err,result){
548 + //result callback
549 +} );
550 +```
551 +
552 +---------------------------------------
553 +
554 +<a name="some" />
555 +### some(arr, iterator, callback)
556 +
557 +__Alias:__ `any`
558 +
559 +Returns `true` if at least one element in the `arr` satisfies an async test.
560 +_The callback for each iterator call only accepts a single argument of `true` or
561 +`false`; it does not accept an error argument first!_ This is in-line with the
562 +way node libraries work with truth tests like `fs.exists`. Once any iterator
563 +call returns `true`, the main `callback` is immediately called.
564 +
565 +__Arguments__
566 +
567 +* `arr` - An array to iterate over.
568 +* `iterator(item, callback)` - A truth test to apply to each item in the array
569 + in parallel. The iterator is passed a callback(truthValue) which must be
570 + called with a boolean argument once it has completed.
571 +* `callback(result)` - A callback which is called as soon as any iterator returns
572 + `true`, or after all the iterator functions have finished. Result will be
573 + either `true` or `false` depending on the values of the async tests.
574 +
575 +__Example__
576 +
577 +```js
578 +async.some(['file1','file2','file3'], fs.exists, function(result){
579 + // if result is true then at least one of the files exists
580 +});
581 +```
582 +
583 +---------------------------------------
584 +
585 +<a name="every" />
586 +### every(arr, iterator, callback)
587 +
588 +__Alias:__ `all`
589 +
590 +Returns `true` if every element in `arr` satisfies an async test.
591 +_The callback for each `iterator` call only accepts a single argument of `true` or
592 +`false`; it does not accept an error argument first!_ This is in-line with the
593 +way node libraries work with truth tests like `fs.exists`.
594 +
595 +__Arguments__
596 +
597 +* `arr` - An array to iterate over.
598 +* `iterator(item, callback)` - A truth test to apply to each item in the array
599 + in parallel. The iterator is passed a callback(truthValue) which must be
600 + called with a boolean argument once it has completed.
601 +* `callback(result)` - A callback which is called after all the `iterator`
602 + functions have finished. Result will be either `true` or `false` depending on
603 + the values of the async tests.
604 +
605 +__Example__
606 +
607 +```js
608 +async.every(['file1','file2','file3'], fs.exists, function(result){
609 + // if result is true then every file exists
610 +});
611 +```
612 +
613 +---------------------------------------
614 +
615 +<a name="concat" />
616 +### concat(arr, iterator, callback)
617 +
618 +Applies `iterator` to each item in `arr`, concatenating the results. Returns the
619 +concatenated list. The `iterator`s are called in parallel, and the results are
620 +concatenated as they return. There is no guarantee that the results array will
621 +be returned in the original order of `arr` passed to the `iterator` function.
622 +
623 +__Arguments__
624 +
625 +* `arr` - An array to iterate over.
626 +* `iterator(item, callback)` - A function to apply to each item in `arr`.
627 + The iterator is passed a `callback(err, results)` which must be called once it
628 + has completed with an error (which can be `null`) and an array of results.
629 +* `callback(err, results)` - A callback which is called after all the `iterator`
630 + functions have finished, or an error occurs. Results is an array containing
631 + the concatenated results of the `iterator` function.
632 +
633 +__Example__
634 +
635 +```js
636 +async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
637 + // files is now a list of filenames that exist in the 3 directories
638 +});
639 +```
640 +
641 +---------------------------------------
642 +
643 +<a name="concatSeries" />
644 +### concatSeries(arr, iterator, callback)
645 +
646 +Same as [`concat`](#concat), but executes in series instead of parallel.
647 +
648 +
649 +## Control Flow
650 +
651 +<a name="series" />
652 +### series(tasks, [callback])
653 +
654 +Run the functions in the `tasks` array in series, each one running once the previous
655 +function has completed. If any functions in the series pass an error to its
656 +callback, no more functions are run, and `callback` is immediately called with the value of the error.
657 +Otherwise, `callback` receives an array of results when `tasks` have completed.
658 +
659 +It is also possible to use an object instead of an array. Each property will be
660 +run as a function, and the results will be passed to the final `callback` as an object
661 +instead of an array. This can be a more readable way of handling results from
662 +[`series`](#series).
663 +
664 +**Note** that while many implementations preserve the order of object properties, the
665 +[ECMAScript Language Specifcation](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
666 +explicitly states that
667 +
668 +> The mechanics and order of enumerating the properties is not specified.
669 +
670 +So if you rely on the order in which your series of functions are executed, and want
671 +this to work on all platforms, consider using an array.
672 +
673 +__Arguments__
674 +
675 +* `tasks` - An array or object containing functions to run, each function is passed
676 + a `callback(err, result)` it must call on completion with an error `err` (which can
677 + be `null`) and an optional `result` value.
678 +* `callback(err, results)` - An optional callback to run once all the functions
679 + have completed. This function gets a results array (or object) containing all
680 + the result arguments passed to the `task` callbacks.
681 +
682 +__Example__
683 +
684 +```js
685 +async.series([
686 + function(callback){
687 + // do some stuff ...
688 + callback(null, 'one');
689 + },
690 + function(callback){
691 + // do some more stuff ...
692 + callback(null, 'two');
693 + }
694 +],
695 +// optional callback
696 +function(err, results){
697 + // results is now equal to ['one', 'two']
698 +});
699 +
700 +
701 +// an example using an object instead of an array
702 +async.series({
703 + one: function(callback){
704 + setTimeout(function(){
705 + callback(null, 1);
706 + }, 200);
707 + },
708 + two: function(callback){
709 + setTimeout(function(){
710 + callback(null, 2);
711 + }, 100);
712 + }
713 +},
714 +function(err, results) {
715 + // results is now equal to: {one: 1, two: 2}
716 +});
717 +```
718 +
719 +---------------------------------------
720 +
721 +<a name="parallel" />
722 +### parallel(tasks, [callback])
723 +
724 +Run the `tasks` array of functions in parallel, without waiting until the previous
725 +function has completed. If any of the functions pass an error to its
726 +callback, the main `callback` is immediately called with the value of the error.
727 +Once the `tasks` have completed, the results are passed to the final `callback` as an
728 +array.
729 +
730 +It is also possible to use an object instead of an array. Each property will be
731 +run as a function and the results will be passed to the final `callback` as an object
732 +instead of an array. This can be a more readable way of handling results from
733 +[`parallel`](#parallel).
734 +
735 +
736 +__Arguments__
737 +
738 +* `tasks` - An array or object containing functions to run. Each function is passed
739 + a `callback(err, result)` which it must call on completion with an error `err`
740 + (which can be `null`) and an optional `result` value.
741 +* `callback(err, results)` - An optional callback to run once all the functions
742 + have completed. This function gets a results array (or object) containing all
743 + the result arguments passed to the task callbacks.
744 +
745 +__Example__
746 +
747 +```js
748 +async.parallel([
749 + function(callback){
750 + setTimeout(function(){
751 + callback(null, 'one');
752 + }, 200);
753 + },
754 + function(callback){
755 + setTimeout(function(){
756 + callback(null, 'two');
757 + }, 100);
758 + }
759 +],
760 +// optional callback
761 +function(err, results){
762 + // the results array will equal ['one','two'] even though
763 + // the second function had a shorter timeout.
764 +});
765 +
766 +
767 +// an example using an object instead of an array
768 +async.parallel({
769 + one: function(callback){
770 + setTimeout(function(){
771 + callback(null, 1);
772 + }, 200);
773 + },
774 + two: function(callback){
775 + setTimeout(function(){
776 + callback(null, 2);
777 + }, 100);
778 + }
779 +},
780 +function(err, results) {
781 + // results is now equals to: {one: 1, two: 2}
782 +});
783 +```
784 +
785 +---------------------------------------
786 +
787 +<a name="parallelLimit" />
788 +### parallelLimit(tasks, limit, [callback])
789 +
790 +The same as [`parallel`](#parallel), only `tasks` are executed in parallel
791 +with a maximum of `limit` tasks executing at any time.
792 +
793 +Note that the `tasks` are not executed in batches, so there is no guarantee that
794 +the first `limit` tasks will complete before any others are started.
795 +
796 +__Arguments__
797 +
798 +* `tasks` - An array or object containing functions to run, each function is passed
799 + a `callback(err, result)` it must call on completion with an error `err` (which can
800 + be `null`) and an optional `result` value.
801 +* `limit` - The maximum number of `tasks` to run at any time.
802 +* `callback(err, results)` - An optional callback to run once all the functions
803 + have completed. This function gets a results array (or object) containing all
804 + the result arguments passed to the `task` callbacks.
805 +
806 +---------------------------------------
807 +
808 +<a name="whilst" />
809 +### whilst(test, fn, callback)
810 +
811 +Repeatedly call `fn`, while `test` returns `true`. Calls `callback` when stopped,
812 +or an error occurs.
813 +
814 +__Arguments__
815 +
816 +* `test()` - synchronous truth test to perform before each execution of `fn`.
817 +* `fn(callback)` - A function which is called each time `test` passes. The function is
818 + passed a `callback(err)`, which must be called once it has completed with an
819 + optional `err` argument.
820 +* `callback(err)` - A callback which is called after the test fails and repeated
821 + execution of `fn` has stopped.
822 +
823 +__Example__
824 +
825 +```js
826 +var count = 0;
827 +
828 +async.whilst(
829 + function () { return count < 5; },
830 + function (callback) {
831 + count++;
832 + setTimeout(callback, 1000);
833 + },
834 + function (err) {
835 + // 5 seconds have passed
836 + }
837 +);
838 +```
839 +
840 +---------------------------------------
841 +
842 +<a name="doWhilst" />
843 +### doWhilst(fn, test, callback)
844 +
845 +The post-check version of [`whilst`](#whilst). To reflect the difference in
846 +the order of operations, the arguments `test` and `fn` are switched.
847 +
848 +`doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
849 +
850 +---------------------------------------
851 +
852 +<a name="until" />
853 +### until(test, fn, callback)
854 +
855 +Repeatedly call `fn` until `test` returns `true`. Calls `callback` when stopped,
856 +or an error occurs.
857 +
858 +The inverse of [`whilst`](#whilst).
859 +
860 +---------------------------------------
861 +
862 +<a name="doUntil" />
863 +### doUntil(fn, test, callback)
864 +
865 +Like [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument ordering differs from `until`.
866 +
867 +---------------------------------------
868 +
869 +<a name="forever" />
870 +### forever(fn, errback)
871 +
872 +Calls the asynchronous function `fn` with a callback parameter that allows it to
873 +call itself again, in series, indefinitely.
874 +
875 +If an error is passed to the callback then `errback` is called with the
876 +error, and execution stops, otherwise it will never be called.
877 +
878 +```js
879 +async.forever(
880 + function(next) {
881 + // next is suitable for passing to things that need a callback(err [, whatever]);
882 + // it will result in this function being called again.
883 + },
884 + function(err) {
885 + // if next is called with a value in its first parameter, it will appear
886 + // in here as 'err', and execution will stop.
887 + }
888 +);
889 +```
890 +
891 +---------------------------------------
892 +
893 +<a name="waterfall" />
894 +### waterfall(tasks, [callback])
895 +
896 +Runs the `tasks` array of functions in series, each passing their results to the next in
897 +the array. However, if any of the `tasks` pass an error to their own callback, the
898 +next function is not executed, and the main `callback` is immediately called with
899 +the error.
900 +
901 +__Arguments__
902 +
903 +* `tasks` - An array of functions to run, each function is passed a
904 + `callback(err, result1, result2, ...)` it must call on completion. The first
905 + argument is an error (which can be `null`) and any further arguments will be
906 + passed as arguments in order to the next task.
907 +* `callback(err, [results])` - An optional callback to run once all the functions
908 + have completed. This will be passed the results of the last task's callback.
909 +
910 +
911 +
912 +__Example__
913 +
914 +```js
915 +async.waterfall([
916 + function(callback){
917 + callback(null, 'one', 'two');
918 + },
919 + function(arg1, arg2, callback){
920 + // arg1 now equals 'one' and arg2 now equals 'two'
921 + callback(null, 'three');
922 + },
923 + function(arg1, callback){
924 + // arg1 now equals 'three'
925 + callback(null, 'done');
926 + }
927 +], function (err, result) {
928 + // result now equals 'done'
929 +});
930 +```
931 +
932 +---------------------------------------
933 +<a name="compose" />
934 +### compose(fn1, fn2...)
935 +
936 +Creates a function which is a composition of the passed asynchronous
937 +functions. Each function consumes the return value of the function that
938 +follows. Composing functions `f()`, `g()`, and `h()` would produce the result of
939 +`f(g(h()))`, only this version uses callbacks to obtain the return values.
940 +
941 +Each function is executed with the `this` binding of the composed function.
942 +
943 +__Arguments__
944 +
945 +* `functions...` - the asynchronous functions to compose
946 +
947 +
948 +__Example__
949 +
950 +```js
951 +function add1(n, callback) {
952 + setTimeout(function () {
953 + callback(null, n + 1);
954 + }, 10);
955 +}
956 +
957 +function mul3(n, callback) {
958 + setTimeout(function () {
959 + callback(null, n * 3);
960 + }, 10);
961 +}
962 +
963 +var add1mul3 = async.compose(mul3, add1);
964 +
965 +add1mul3(4, function (err, result) {
966 + // result now equals 15
967 +});
968 +```
969 +
970 +---------------------------------------
971 +<a name="seq" />
972 +### seq(fn1, fn2...)
973 +
974 +Version of the compose function that is more natural to read.
975 +Each following function consumes the return value of the latter function.
976 +
977 +Each function is executed with the `this` binding of the composed function.
978 +
979 +__Arguments__
980 +
981 +* functions... - the asynchronous functions to compose
982 +
983 +
984 +__Example__
985 +
986 +```js
987 +// Requires lodash (or underscore), express3 and dresende's orm2.
988 +// Part of an app, that fetches cats of the logged user.
989 +// This example uses `seq` function to avoid overnesting and error
990 +// handling clutter.
991 +app.get('/cats', function(request, response) {
992 + function handleError(err, data, callback) {
993 + if (err) {
994 + console.error(err);
995 + response.json({ status: 'error', message: err.message });
996 + }
997 + else {
998 + callback(data);
999 + }
1000 + }
1001 + var User = request.models.User;
1002 + async.seq(
1003 + _.bind(User.get, User), // 'User.get' has signature (id, callback(err, data))
1004 + handleError,
1005 + function(user, fn) {
1006 + user.getCats(fn); // 'getCats' has signature (callback(err, data))
1007 + },
1008 + handleError,
1009 + function(cats) {
1010 + response.json({ status: 'ok', message: 'Cats found', data: cats });
1011 + }
1012 + )(req.session.user_id);
1013 + }
1014 +});
1015 +```
1016 +
1017 +---------------------------------------
1018 +<a name="applyEach" />
1019 +### applyEach(fns, args..., callback)
1020 +
1021 +Applies the provided arguments to each function in the array, calling
1022 +`callback` after all functions have completed. If you only provide the first
1023 +argument, then it will return a function which lets you pass in the
1024 +arguments as if it were a single function call.
1025 +
1026 +__Arguments__
1027 +
1028 +* `fns` - the asynchronous functions to all call with the same arguments
1029 +* `args...` - any number of separate arguments to pass to the function
1030 +* `callback` - the final argument should be the callback, called when all
1031 + functions have completed processing
1032 +
1033 +
1034 +__Example__
1035 +
1036 +```js
1037 +async.applyEach([enableSearch, updateSchema], 'bucket', callback);
1038 +
1039 +// partial application example:
1040 +async.each(
1041 + buckets,
1042 + async.applyEach([enableSearch, updateSchema]),
1043 + callback
1044 +);
1045 +```
1046 +
1047 +---------------------------------------
1048 +
1049 +<a name="applyEachSeries" />
1050 +### applyEachSeries(arr, iterator, callback)
1051 +
1052 +The same as [`applyEach`](#applyEach) only the functions are applied in series.
1053 +
1054 +---------------------------------------
1055 +
1056 +<a name="queue" />
1057 +### queue(worker, concurrency)
1058 +
1059 +Creates a `queue` object with the specified `concurrency`. Tasks added to the
1060 +`queue` are processed in parallel (up to the `concurrency` limit). If all
1061 +`worker`s are in progress, the task is queued until one becomes available.
1062 +Once a `worker` completes a `task`, that `task`'s callback is called.
1063 +
1064 +__Arguments__
1065 +
1066 +* `worker(task, callback)` - An asynchronous function for processing a queued
1067 + task, which must call its `callback(err)` argument when finished, with an
1068 + optional `error` as an argument.
1069 +* `concurrency` - An `integer` for determining how many `worker` functions should be
1070 + run in parallel.
1071 +
1072 +__Queue objects__
1073 +
1074 +The `queue` object returned by this function has the following properties and
1075 +methods:
1076 +
1077 +* `length()` - a function returning the number of items waiting to be processed.
1078 +* `started` - a function returning whether or not any items have been pushed and processed by the queue
1079 +* `running()` - a function returning the number of items currently being processed.
1080 +* `idle()` - a function returning false if there are items waiting or being processed, or true if not.
1081 +* `concurrency` - an integer for determining how many `worker` functions should be
1082 + run in parallel. This property can be changed after a `queue` is created to
1083 + alter the concurrency on-the-fly.
1084 +* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once
1085 + the `worker` has finished processing the task. Instead of a single task, a `tasks` array
1086 + can be submitted. The respective callback is used for every task in the list.
1087 +* `unshift(task, [callback])` - add a new task to the front of the `queue`.
1088 +* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit,
1089 + and further tasks will be queued.
1090 +* `empty` - a callback that is called when the last item from the `queue` is given to a `worker`.
1091 +* `drain` - a callback that is called when the last item from the `queue` has returned from the `worker`.
1092 +* `paused` - a boolean for determining whether the queue is in a paused state
1093 +* `pause()` - a function that pauses the processing of tasks until `resume()` is called.
1094 +* `resume()` - a function that resumes the processing of queued tasks when the queue is paused.
1095 +* `kill()` - a function that empties remaining tasks from the queue forcing it to go idle.
1096 +
1097 +__Example__
1098 +
1099 +```js
1100 +// create a queue object with concurrency 2
1101 +
1102 +var q = async.queue(function (task, callback) {
1103 + console.log('hello ' + task.name);
1104 + callback();
1105 +}, 2);
1106 +
1107 +
1108 +// assign a callback
1109 +q.drain = function() {
1110 + console.log('all items have been processed');
1111 +}
1112 +
1113 +// add some items to the queue
1114 +
1115 +q.push({name: 'foo'}, function (err) {
1116 + console.log('finished processing foo');
1117 +});
1118 +q.push({name: 'bar'}, function (err) {
1119 + console.log('finished processing bar');
1120 +});
1121 +
1122 +// add some items to the queue (batch-wise)
1123 +
1124 +q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
1125 + console.log('finished processing bar');
1126 +});
1127 +
1128 +// add some items to the front of the queue
1129 +
1130 +q.unshift({name: 'bar'}, function (err) {
1131 + console.log('finished processing bar');
1132 +});
1133 +```
1134 +
1135 +
1136 +---------------------------------------
1137 +
1138 +<a name="priorityQueue" />
1139 +### priorityQueue(worker, concurrency)
1140 +
1141 +The same as [`queue`](#queue) only tasks are assigned a priority and completed in ascending priority order. There are two differences between `queue` and `priorityQueue` objects:
1142 +
1143 +* `push(task, priority, [callback])` - `priority` should be a number. If an array of
1144 + `tasks` is given, all tasks will be assigned the same priority.
1145 +* The `unshift` method was removed.
1146 +
1147 +---------------------------------------
1148 +
1149 +<a name="cargo" />
1150 +### cargo(worker, [payload])
1151 +
1152 +Creates a `cargo` object with the specified payload. Tasks added to the
1153 +cargo will be processed altogether (up to the `payload` limit). If the
1154 +`worker` is in progress, the task is queued until it becomes available. Once
1155 +the `worker` has completed some tasks, each callback of those tasks is called.
1156 +Check out [this animation](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) for how `cargo` and `queue` work.
1157 +
1158 +While [queue](#queue) passes only one task to one of a group of workers
1159 +at a time, cargo passes an array of tasks to a single worker, repeating
1160 +when the worker is finished.
1161 +
1162 +__Arguments__
1163 +
1164 +* `worker(tasks, callback)` - An asynchronous function for processing an array of
1165 + queued tasks, which must call its `callback(err)` argument when finished, with
1166 + an optional `err` argument.
1167 +* `payload` - An optional `integer` for determining how many tasks should be
1168 + processed per round; if omitted, the default is unlimited.
1169 +
1170 +__Cargo objects__
1171 +
1172 +The `cargo` object returned by this function has the following properties and
1173 +methods:
1174 +
1175 +* `length()` - A function returning the number of items waiting to be processed.
1176 +* `payload` - An `integer` for determining how many tasks should be
1177 + process per round. This property can be changed after a `cargo` is created to
1178 + alter the payload on-the-fly.
1179 +* `push(task, [callback])` - Adds `task` to the `queue`. The callback is called
1180 + once the `worker` has finished processing the task. Instead of a single task, an array of `tasks`
1181 + can be submitted. The respective callback is used for every task in the list.
1182 +* `saturated` - A callback that is called when the `queue.length()` hits the concurrency and further tasks will be queued.
1183 +* `empty` - A callback that is called when the last item from the `queue` is given to a `worker`.
1184 +* `drain` - A callback that is called when the last item from the `queue` has returned from the `worker`.
1185 +
1186 +__Example__
1187 +
1188 +```js
1189 +// create a cargo object with payload 2
1190 +
1191 +var cargo = async.cargo(function (tasks, callback) {
1192 + for(var i=0; i<tasks.length; i++){
1193 + console.log('hello ' + tasks[i].name);
1194 + }
1195 + callback();
1196 +}, 2);
1197 +
1198 +
1199 +// add some items
1200 +
1201 +cargo.push({name: 'foo'}, function (err) {
1202 + console.log('finished processing foo');
1203 +});
1204 +cargo.push({name: 'bar'}, function (err) {
1205 + console.log('finished processing bar');
1206 +});
1207 +cargo.push({name: 'baz'}, function (err) {
1208 + console.log('finished processing baz');
1209 +});
1210 +```
1211 +
1212 +---------------------------------------
1213 +
1214 +<a name="auto" />
1215 +### auto(tasks, [callback])
1216 +
1217 +Determines the best order for running the functions in `tasks`, based on their
1218 +requirements. Each function can optionally depend on other functions being completed
1219 +first, and each function is run as soon as its requirements are satisfied.
1220 +
1221 +If any of the functions pass an error to their callback, it will not
1222 +complete (so any other functions depending on it will not run), and the main
1223 +`callback` is immediately called with the error. Functions also receive an
1224 +object containing the results of functions which have completed so far.
1225 +
1226 +Note, all functions are called with a `results` object as a second argument,
1227 +so it is unsafe to pass functions in the `tasks` object which cannot handle the
1228 +extra argument.
1229 +
1230 +For example, this snippet of code:
1231 +
1232 +```js
1233 +async.auto({
1234 + readData: async.apply(fs.readFile, 'data.txt', 'utf-8')
1235 +}, callback);
1236 +```
1237 +
1238 +will have the effect of calling `readFile` with the results object as the last
1239 +argument, which will fail:
1240 +
1241 +```js
1242 +fs.readFile('data.txt', 'utf-8', cb, {});
1243 +```
1244 +
1245 +Instead, wrap the call to `readFile` in a function which does not forward the
1246 +`results` object:
1247 +
1248 +```js
1249 +async.auto({
1250 + readData: function(cb, results){
1251 + fs.readFile('data.txt', 'utf-8', cb);
1252 + }
1253 +}, callback);
1254 +```
1255 +
1256 +__Arguments__
1257 +
1258 +* `tasks` - An object. Each of its properties is either a function or an array of
1259 + requirements, with the function itself the last item in the array. The object's key
1260 + of a property serves as the name of the task defined by that property,
1261 + i.e. can be used when specifying requirements for other tasks.
1262 + The function receives two arguments: (1) a `callback(err, result)` which must be
1263 + called when finished, passing an `error` (which can be `null`) and the result of
1264 + the function's execution, and (2) a `results` object, containing the results of
1265 + the previously executed functions.
1266 +* `callback(err, results)` - An optional callback which is called when all the
1267 + tasks have been completed. It receives the `err` argument if any `tasks`
1268 + pass an error to their callback. Results are always returned; however, if
1269 + an error occurs, no further `tasks` will be performed, and the results
1270 + object will only contain partial results.
1271 +
1272 +
1273 +__Example__
1274 +
1275 +```js
1276 +async.auto({
1277 + get_data: function(callback){
1278 + console.log('in get_data');
1279 + // async code to get some data
1280 + callback(null, 'data', 'converted to array');
1281 + },
1282 + make_folder: function(callback){
1283 + console.log('in make_folder');
1284 + // async code to create a directory to store a file in
1285 + // this is run at the same time as getting the data
1286 + callback(null, 'folder');
1287 + },
1288 + write_file: ['get_data', 'make_folder', function(callback, results){
1289 + console.log('in write_file', JSON.stringify(results));
1290 + // once there is some data and the directory exists,
1291 + // write the data to a file in the directory
1292 + callback(null, 'filename');
1293 + }],
1294 + email_link: ['write_file', function(callback, results){
1295 + console.log('in email_link', JSON.stringify(results));
1296 + // once the file is written let's email a link to it...
1297 + // results.write_file contains the filename returned by write_file.
1298 + callback(null, {'file':results.write_file, 'email':'user@example.com'});
1299 + }]
1300 +}, function(err, results) {
1301 + console.log('err = ', err);
1302 + console.log('results = ', results);
1303 +});
1304 +```
1305 +
1306 +This is a fairly trivial example, but to do this using the basic parallel and
1307 +series functions would look like this:
1308 +
1309 +```js
1310 +async.parallel([
1311 + function(callback){
1312 + console.log('in get_data');
1313 + // async code to get some data
1314 + callback(null, 'data', 'converted to array');
1315 + },
1316 + function(callback){
1317 + console.log('in make_folder');
1318 + // async code to create a directory to store a file in
1319 + // this is run at the same time as getting the data
1320 + callback(null, 'folder');
1321 + }
1322 +],
1323 +function(err, results){
1324 + async.series([
1325 + function(callback){
1326 + console.log('in write_file', JSON.stringify(results));
1327 + // once there is some data and the directory exists,
1328 + // write the data to a file in the directory
1329 + results.push('filename');
1330 + callback(null);
1331 + },
1332 + function(callback){
1333 + console.log('in email_link', JSON.stringify(results));
1334 + // once the file is written let's email a link to it...
1335 + callback(null, {'file':results.pop(), 'email':'user@example.com'});
1336 + }
1337 + ]);
1338 +});
1339 +```
1340 +
1341 +For a complicated series of `async` tasks, using the [`auto`](#auto) function makes adding
1342 +new tasks much easier (and the code more readable).
1343 +
1344 +
1345 +---------------------------------------
1346 +
1347 +<a name="retry" />
1348 +### retry([times = 5], task, [callback])
1349 +
1350 +Attempts to get a successful response from `task` no more than `times` times before
1351 +returning an error. If the task is successful, the `callback` will be passed the result
1352 +of the successfull task. If all attemps fail, the callback will be passed the error and
1353 +result (if any) of the final attempt.
1354 +
1355 +__Arguments__
1356 +
1357 +* `times` - An integer indicating how many times to attempt the `task` before giving up. Defaults to 5.
1358 +* `task(callback, results)` - A function which receives two arguments: (1) a `callback(err, result)`
1359 + which must be called when finished, passing `err` (which can be `null`) and the `result` of
1360 + the function's execution, and (2) a `results` object, containing the results of
1361 + the previously executed functions (if nested inside another control flow).
1362 +* `callback(err, results)` - An optional callback which is called when the
1363 + task has succeeded, or after the final failed attempt. It receives the `err` and `result` arguments of the last attempt at completing the `task`.
1364 +
1365 +The [`retry`](#retry) function can be used as a stand-alone control flow by passing a
1366 +callback, as shown below:
1367 +
1368 +```js
1369 +async.retry(3, apiMethod, function(err, result) {
1370 + // do something with the result
1371 +});
1372 +```
1373 +
1374 +It can also be embeded within other control flow functions to retry individual methods
1375 +that are not as reliable, like this:
1376 +
1377 +```js
1378 +async.auto({
1379 + users: api.getUsers.bind(api),
1380 + payments: async.retry(3, api.getPayments.bind(api))
1381 +}, function(err, results) {
1382 + // do something with the results
1383 +});
1384 +```
1385 +
1386 +
1387 +---------------------------------------
1388 +
1389 +<a name="iterator" />
1390 +### iterator(tasks)
1391 +
1392 +Creates an iterator function which calls the next function in the `tasks` array,
1393 +returning a continuation to call the next one after that. It's also possible to
1394 +“peek” at the next iterator with `iterator.next()`.
1395 +
1396 +This function is used internally by the `async` module, but can be useful when
1397 +you want to manually control the flow of functions in series.
1398 +
1399 +__Arguments__
1400 +
1401 +* `tasks` - An array of functions to run.
1402 +
1403 +__Example__
1404 +
1405 +```js
1406 +var iterator = async.iterator([
1407 + function(){ sys.p('one'); },
1408 + function(){ sys.p('two'); },
1409 + function(){ sys.p('three'); }
1410 +]);
1411 +
1412 +node> var iterator2 = iterator();
1413 +'one'
1414 +node> var iterator3 = iterator2();
1415 +'two'
1416 +node> iterator3();
1417 +'three'
1418 +node> var nextfn = iterator2.next();
1419 +node> nextfn();
1420 +'three'
1421 +```
1422 +
1423 +---------------------------------------
1424 +
1425 +<a name="apply" />
1426 +### apply(function, arguments..)
1427 +
1428 +Creates a continuation function with some arguments already applied.
1429 +
1430 +Useful as a shorthand when combined with other control flow functions. Any arguments
1431 +passed to the returned function are added to the arguments originally passed
1432 +to apply.
1433 +
1434 +__Arguments__
1435 +
1436 +* `function` - The function you want to eventually apply all arguments to.
1437 +* `arguments...` - Any number of arguments to automatically apply when the
1438 + continuation is called.
1439 +
1440 +__Example__
1441 +
1442 +```js
1443 +// using apply
1444 +
1445 +async.parallel([
1446 + async.apply(fs.writeFile, 'testfile1', 'test1'),
1447 + async.apply(fs.writeFile, 'testfile2', 'test2'),
1448 +]);
1449 +
1450 +
1451 +// the same process without using apply
1452 +
1453 +async.parallel([
1454 + function(callback){
1455 + fs.writeFile('testfile1', 'test1', callback);
1456 + },
1457 + function(callback){
1458 + fs.writeFile('testfile2', 'test2', callback);
1459 + }
1460 +]);
1461 +```
1462 +
1463 +It's possible to pass any number of additional arguments when calling the
1464 +continuation:
1465 +
1466 +```js
1467 +node> var fn = async.apply(sys.puts, 'one');
1468 +node> fn('two', 'three');
1469 +one
1470 +two
1471 +three
1472 +```
1473 +
1474 +---------------------------------------
1475 +
1476 +<a name="nextTick" />
1477 +### nextTick(callback)
1478 +
1479 +Calls `callback` on a later loop around the event loop. In Node.js this just
1480 +calls `process.nextTick`; in the browser it falls back to `setImmediate(callback)`
1481 +if available, otherwise `setTimeout(callback, 0)`, which means other higher priority
1482 +events may precede the execution of `callback`.
1483 +
1484 +This is used internally for browser-compatibility purposes.
1485 +
1486 +__Arguments__
1487 +
1488 +* `callback` - The function to call on a later loop around the event loop.
1489 +
1490 +__Example__
1491 +
1492 +```js
1493 +var call_order = [];
1494 +async.nextTick(function(){
1495 + call_order.push('two');
1496 + // call_order now equals ['one','two']
1497 +});
1498 +call_order.push('one')
1499 +```
1500 +
1501 +<a name="times" />
1502 +### times(n, callback)
1503 +
1504 +Calls the `callback` function `n` times, and accumulates results in the same manner
1505 +you would use with [`map`](#map).
1506 +
1507 +__Arguments__
1508 +
1509 +* `n` - The number of times to run the function.
1510 +* `callback` - The function to call `n` times.
1511 +
1512 +__Example__
1513 +
1514 +```js
1515 +// Pretend this is some complicated async factory
1516 +var createUser = function(id, callback) {
1517 + callback(null, {
1518 + id: 'user' + id
1519 + })
1520 +}
1521 +// generate 5 users
1522 +async.times(5, function(n, next){
1523 + createUser(n, function(err, user) {
1524 + next(err, user)
1525 + })
1526 +}, function(err, users) {
1527 + // we should now have 5 users
1528 +});
1529 +```
1530 +
1531 +<a name="timesSeries" />
1532 +### timesSeries(n, callback)
1533 +
1534 +The same as [`times`](#times), only the iterator is applied to each item in `arr` in
1535 +series. The next `iterator` is only called once the current one has completed.
1536 +The results array will be in the same order as the original.
1537 +
1538 +
1539 +## Utils
1540 +
1541 +<a name="memoize" />
1542 +### memoize(fn, [hasher])
1543 +
1544 +Caches the results of an `async` function. When creating a hash to store function
1545 +results against, the callback is omitted from the hash and an optional hash
1546 +function can be used.
1547 +
1548 +The cache of results is exposed as the `memo` property of the function returned
1549 +by `memoize`.
1550 +
1551 +__Arguments__
1552 +
1553 +* `fn` - The function to proxy and cache results from.
1554 +* `hasher` - Tn optional function for generating a custom hash for storing
1555 + results. It has all the arguments applied to it apart from the callback, and
1556 + must be synchronous.
1557 +
1558 +__Example__
1559 +
1560 +```js
1561 +var slow_fn = function (name, callback) {
1562 + // do something
1563 + callback(null, result);
1564 +};
1565 +var fn = async.memoize(slow_fn);
1566 +
1567 +// fn can now be used as if it were slow_fn
1568 +fn('some name', function () {
1569 + // callback
1570 +});
1571 +```
1572 +
1573 +<a name="unmemoize" />
1574 +### unmemoize(fn)
1575 +
1576 +Undoes a [`memoize`](#memoize)d function, reverting it to the original, unmemoized
1577 +form. Handy for testing.
1578 +
1579 +__Arguments__
1580 +
1581 +* `fn` - the memoized function
1582 +
1583 +<a name="log" />
1584 +### log(function, arguments)
1585 +
1586 +Logs the result of an `async` function to the `console`. Only works in Node.js or
1587 +in browsers that support `console.log` and `console.error` (such as FF and Chrome).
1588 +If multiple arguments are returned from the async function, `console.log` is
1589 +called on each argument in order.
1590 +
1591 +__Arguments__
1592 +
1593 +* `function` - The function you want to eventually apply all arguments to.
1594 +* `arguments...` - Any number of arguments to apply to the function.
1595 +
1596 +__Example__
1597 +
1598 +```js
1599 +var hello = function(name, callback){
1600 + setTimeout(function(){
1601 + callback(null, 'hello ' + name);
1602 + }, 1000);
1603 +};
1604 +```
1605 +```js
1606 +node> async.log(hello, 'world');
1607 +'hello world'
1608 +```
1609 +
1610 +---------------------------------------
1611 +
1612 +<a name="dir" />
1613 +### dir(function, arguments)
1614 +
1615 +Logs the result of an `async` function to the `console` using `console.dir` to
1616 +display the properties of the resulting object. Only works in Node.js or
1617 +in browsers that support `console.dir` and `console.error` (such as FF and Chrome).
1618 +If multiple arguments are returned from the async function, `console.dir` is
1619 +called on each argument in order.
1620 +
1621 +__Arguments__
1622 +
1623 +* `function` - The function you want to eventually apply all arguments to.
1624 +* `arguments...` - Any number of arguments to apply to the function.
1625 +
1626 +__Example__
1627 +
1628 +```js
1629 +var hello = function(name, callback){
1630 + setTimeout(function(){
1631 + callback(null, {hello: name});
1632 + }, 1000);
1633 +};
1634 +```
1635 +```js
1636 +node> async.dir(hello, 'world');
1637 +{hello: 'world'}
1638 +```
1639 +
1640 +---------------------------------------
1641 +
1642 +<a name="noConflict" />
1643 +### noConflict()
1644 +
1645 +Changes the value of `async` back to its original value, returning a reference to the
1646 +`async` object.
1 +{
2 + "name": "async",
3 + "repo": "caolan/async",
4 + "description": "Higher-order functions and common patterns for asynchronous code",
5 + "version": "0.1.23",
6 + "keywords": [],
7 + "dependencies": {},
8 + "development": {},
9 + "main": "lib/async.js",
10 + "scripts": [ "lib/async.js" ]
11 +}
1 +{
2 + "name": "async",
3 + "description": "Higher-order functions and common patterns for asynchronous code",
4 + "main": "./lib/async",
5 + "author": {
6 + "name": "Caolan McMahon"
7 + },
8 + "version": "0.9.0",
9 + "repository": {
10 + "type": "git",
11 + "url": "https://github.com/caolan/async.git"
12 + },
13 + "bugs": {
14 + "url": "https://github.com/caolan/async/issues"
15 + },
16 + "licenses": [
17 + {
18 + "type": "MIT",
19 + "url": "https://github.com/caolan/async/raw/master/LICENSE"
20 + }
21 + ],
22 + "devDependencies": {
23 + "nodeunit": ">0.0.0",
24 + "uglify-js": "1.2.x",
25 + "nodelint": ">0.0.0"
26 + },
27 + "jam": {
28 + "main": "lib/async.js",
29 + "include": [
30 + "lib/async.js",
31 + "README.md",
32 + "LICENSE"
33 + ]
34 + },
35 + "scripts": {
36 + "test": "nodeunit test/test-async.js"
37 + },
38 + "homepage": "https://github.com/caolan/async",
39 + "_id": "async@0.9.0",
40 + "dist": {
41 + "shasum": "ac3613b1da9bed1b47510bb4651b8931e47146c7",
42 + "tarball": "http://registry.npmjs.org/async/-/async-0.9.0.tgz"
43 + },
44 + "_from": "async@",
45 + "_npmVersion": "1.4.3",
46 + "_npmUser": {
47 + "name": "caolan",
48 + "email": "caolan.mcmahon@gmail.com"
49 + },
50 + "maintainers": [
51 + {
52 + "name": "caolan",
53 + "email": "caolan@caolanmcmahon.com"
54 + }
55 + ],
56 + "directories": {},
57 + "_shasum": "ac3613b1da9bed1b47510bb4651b8931e47146c7",
58 + "_resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz",
59 + "readme": "ERROR: No README data found!"
60 +}
1 +before_install:
2 + - sudo apt-get update
3 + - sudo apt-get install imagemagick graphicsmagick
4 +language: node_js
5 +node_js:
6 + - "0.8"
7 + - "0.10"
1 +1.17.0 / 2014-10-28
2 +==================
3 +
4 + * changed: extended compare callback also returns the file names #297 [mastix](https://github.com/mastix)
5 + * changed: pass spawn crash to callback #306 [medikoo](https://github.com/medikoo)
6 + * changed: geometry supports arbitary string as first argument #330 [jdiez17](https://github.com/jdiez17)
7 + * added: support for repage+ option #275 [desigens](https://github.com/desigens)
8 + * added: added the dissolve command #300 [microadm](https://github.com/microadam)
9 + * added: composite method #332 [jdiez17](https://github.com/jdiez17)
10 + * fixed: cannot set tolerance to 0 #302 [rwky](https://github.com/rwky)
11 + * fixed: handle empty buffers #330 [alcidesv](https://github.com/alcidesv)
12 +
13 +1.16.0 / 2014-05-09
14 +==================
15 +
16 + * fixed; dropped "+" when 0 passed as vertical roll amt #267 [dwtkns](https://github.com/dwtkns)
17 + * added; highlight-style support #272 [fdecampredon](https://github.com/fdecampredon)
18 +
19 +1.15.0 / 2014-05-03
20 +===================
21 +
22 + * changed; gm.compare logic to always run the mse comparison as expected #258 [Vokkim](https://github.com/Vokkim)
23 + * added; `tolerance` to gm.compare options object #258 [Vokkim](https://github.com/Vokkim)
24 + * added; option to set ImageMagick application path explicitly #250 (akreitals)
25 + * fixed; gm.compare: support values like 9.51582e-05 #260 [normanrz](https://github.com/normanrz)
26 + * README: add call for maintainers
27 +
28 +1.14.2 / 2013-12-24
29 +===================
30 +
31 +* fixed; background is now a setting #246 (PEM--)
32 +
33 +1.14.1 / 2013-12-09
34 +===================
35 +
36 +* fixed; identify -verbose colon behavior #240 ludow
37 +
38 +1.14.0 / 2013-12-04
39 +===================
40 +
41 +* added; compare method for imagemagick (longlho)
42 +
43 +1.13.3 / 2013-10-22
44 +===================
45 +
46 +* fixed; escape diffOptions.file in compare (dwabyick)
47 +
48 +1.13.2 / 2013-10-18
49 +===================
50 +
51 +* fixed; density is a setting not an operator
52 +
53 +1.13.1 / 2013-09-15
54 +===================
55 +
56 +* added; boolean for % crop
57 +
58 +1.13.0 / 2013-09-07
59 +===================
60 +
61 +* added; morph more than two images (overra)
62 +
63 +1.12.2 / 2013-08-29
64 +===================
65 +
66 +* fixed; fallback to through in node 0.8
67 +
68 +1.12.1 / 2013-08-29 (unpublished)
69 +===================
70 +
71 +* refactor; replace through with stream.PassThrough
72 +
73 +1.12.0 / 2013-08-27
74 +===================
75 +
76 +* added; diff image output file (chenglou)
77 +
78 +1.11.1 / 2013-08-17
79 +===================
80 +
81 +* added; proto.selectFrame(#)
82 +* fixed; getters should not ignore frame selection
83 +
84 +1.11.0 / 2013-07-23
85 +===================
86 +
87 +* added; optional formatting string for gm().identify(format, callback) (tornillo)
88 +* removed; error messages when gm/im binary is not installed
89 +
90 +1.10.0 / 2013-06-27
91 +===================
92 +
93 +* refactor; use native `-auto-orient` for imagemagick
94 +
95 +1.9.2 / 2013-06-12
96 +==================
97 +
98 + * refactor; move `streamToBuffer` to a separate module
99 + * fixed; .stream(format) without a callback
100 +
101 +1.9.1 / 2013-05-07
102 +==================
103 +
104 + * fixed; gm().resize(width) always only resizes width
105 + * fixed; gm('img.gif').format() returns the format of the first frame
106 +
107 +1.9.0 / 2013-04-21
108 +==================
109 +
110 + * added; node v0.10 support
111 + * removed; node < v0.8 support - `Buffer.concat()`
112 + * tests; all tests now run on Travis
113 + * added; gm().stream() returns a stream when no callback is present
114 + * added; gm().toBuffer(callback)
115 + * fixed; gm().size() only returns the size of the first frame of a GIF
116 +
117 +1.8.2 / 2013-03-07
118 +==================
119 +
120 + * include source path in identify data #126 [soupdiver](https://github.com/soupdiver)
121 +
122 +1.8.1 / 2012-12-21
123 +==================
124 +
125 + * Avoid losing already set arguments on identify #105 #113 #109 [JNissi](https://github.com/JNissi)
126 + * tests; add autoOrient + thumb() test
127 + * tests; add test case for #113
128 + * tests; added test for #109
129 + * tests; add resize on buffer test
130 +
131 +1.8.0 / 2012-12-14
132 +==================
133 +
134 + * added; geometry support to scale() #98
135 + * removed; incorrect/broken dissolve() method (never worked)
136 + * fixed; handle child_proc error when using Buffer input #109
137 + * fixed; use of Buffers with identify() #109
138 + * fixed; no longer include -size arg with resize() #98
139 + * fixed; remove -size arg from extent() #103
140 + * fixed; magnify support
141 + * fixed; autoOrient to work with all types of exif orientations [dambalah](https://github.com/dambalah) #108
142 + * tests; npm test runs unit only (now compatible with travis)
143 + * tests; fix magnify test on imagemagick
144 + * tests; added for cmd line args
145 +
146 +1.7.0 / 2012-12-06
147 +==================
148 +
149 + * added; gm.compare support
150 + * added; passing Buffers directly [danmilon](https://github.com/danmilon)
151 +
152 +1.6.1 / 2012-11-13
153 +==================
154 +
155 + * fixed regression; only pass additional params on error #96
156 +
157 +1.6.0 / 2012-11-10
158 +==================
159 +
160 + * changed; rename internal buffer to _buffer #88 [kof](https://github.com/kof)
161 + * changed; optimized identify getters (format, depth, size, color, filesize). #83 please read this for details: https://github.com/aheckmann/gm/commit/8fcf3f8f84a02cc2001da874cbebb89bf7084409
162 + * added; visionmedia/debug support
163 + * added; `gm convert -thumbnail` support. _differs from thumb()._ [danmilon](https://github.com/danmilon)
164 + * fixed; -rotate 0 support #90
165 + * fixed; multi-execution of same gm instance arguments corruption
166 + * fixed; gracefully handle parser errors #94 [eldilibra](https://github.com/eldilibra)
167 +
168 +1.5.1 / 2012-10-02
169 +==================
170 +
171 + * fixed; passing multiple paths to append() #77
172 +
173 +1.5.0 / 2012-09-15
174 +==================
175 +
176 + * fixed; callback scope
177 + * fixed; append() usage #77
178 +
179 +1.4.2 / 2012-08-17
180 +==================
181 +
182 + * fixed; identify parsing for ImageMagick exif data (#58)
183 + * fixed; when in imageMagick mode, complain about missing imageMagick [bcherry](https://github.com/bcherry) (#73)
184 + * added; tests
185 +
186 +1.4.1 / 2012-07-31
187 +==================
188 +
189 + * fixed; scenes() args
190 + * fixed; accept the left-to-right arg of append()
191 + * added; _subCommand
192 +
193 +## v1.4 - 07/28/2012
194 +
195 + * added; adjoin() [Math-]
196 + * added; affine() [Math-]
197 + * added; append() [Math-]
198 + * added; authenticate() [Math-]
199 + * added; average() [Math-]
200 + * added; backdrop() [Math-]
201 + * added; blackThreshold() [Math-]
202 + * added; bluePrimary() [Math-]
203 + * added; border() [Math-]
204 + * added; borderColor() [Math-]
205 + * added; box() [Math-]
206 + * added; channel() [Math-]
207 + * added; clip() [Math-]
208 + * added; coalesce() [Math-]
209 + * added; colorMap() [Math-]
210 + * added; compose() [Math-]
211 + * added; compress() [Math-]
212 + * added; convolve() [Math-]
213 + * added; createDirectories() [Math-]
214 + * added; deconstruct() [Math-]
215 + * added; delay() [Math-]
216 + * added; define() [Math-]
217 + * added; displace() [Math-]
218 + * added; display() [Math-]
219 + * added; dispose() [Math-]
220 + * added; disolve() [Math-]
221 + * added; encoding() [Math-]
222 + * added; endian() [Math-]
223 + * added; file() [Math-]
224 + * added; flatten() [Math-]
225 + * added; foreground() [Math-]
226 + * added; frame() [Math-]
227 + * added; fuzz() [Math-]
228 + * added; gaussian() [Math-]
229 + * added; geometry() [Math-]
230 + * added; greenPrimary() [Math-]
231 + * added; highlightColor() [Math-]
232 + * added; highlightStyle() [Math-]
233 + * added; iconGeometry() [Math-]
234 + * added; intent() [Math-]
235 + * added; lat() [Math-]
236 + * added; level() [Math-]
237 + * added; list() [Math-]
238 + * added; log() [Math-]
239 + * added; map() [Math-]
240 + * added; matte() [Math-]
241 + * added; matteColor() [Math-]
242 + * added; mask() [Math-]
243 + * added; maximumError() [Math-]
244 + * added; mode() [Math-]
245 + * added; monitor() [Math-]
246 + * added; mosaic() [Math-]
247 + * added; motionBlur() [Math-]
248 + * added; name() [Math-]
249 + * added; noop() [Math-]
250 + * added; normalize() [Math-]
251 + * added; opaque() [Math-]
252 + * added; operator() [Math-]
253 + * added; orderedDither() [Math-]
254 + * added; outputDirectory() [Math-]
255 + * added; page() [Math-]
256 + * added; pause() [Math-]
257 + * added; pen() [Math-]
258 + * added; ping() [Math-]
259 + * added; pointSize() [Math-]
260 + * added; preview() [Math-]
261 + * added; process() [Math-]
262 + * added; profile() [Math-]
263 + * added; progress() [Math-]
264 + * added; rawSize() [Math-]
265 + * added; randomThreshold() [Math-]
266 + * added; recolor() [Math-]
267 + * added; redPrimary() [Math-]
268 + * added; remote() [Math-]
269 + * added; render() [Math-]
270 + * added; repage() [Math-]
271 + * added; sample() [Math-]
272 + * added; samplingFactor() [Math-]
273 + * added; scene() [Math-]
274 + * added; scenes() [Math-]
275 + * added; screen() [Math-]
276 + * added; segment() [Math-]
277 + * added; set() [Math-]
278 + * added; shade() [Math-]
279 + * added; shadow() [Math-]
280 + * added; sharedMemory() [Math-]
281 + * added; shave() [Math-]
282 + * added; shear() [Math-]
283 + * added; silent() [Math-]
284 + * added; snaps() [Math-]
285 + * added; stagano() [Math-]
286 + * added; stereo() [Math-]
287 + * added; textFont() [Math-]
288 + * added; texture() [Math-]
289 + * added; threshold() [Math-]
290 + * added; tile() [Math-]
291 + * added; transform() [Math-]
292 + * added; transparent() [Math-]
293 + * added; treeDepth() [Math-]
294 + * added; update() [Math-]
295 + * added; units() [Math-]
296 + * added; unsharp() [Math-]
297 + * added; usePixmap() [Math-]
298 + * added; view() [Math-]
299 + * added; virtualPixel() [Math-]
300 + * added; visual() [Math-]
301 + * added; watermark() [Math-]
302 + * added; wave() [Math-]
303 + * added; whitePoint() [Math-]
304 + * added; whiteThreshold() [Math-]
305 + * added; window() [Math-]
306 + * added; windowGroup() [Math-]
307 +
308 +## v1.3.2 - 06/22/2012
309 +
310 + * added; node >= 0.7/0.8 compat
311 +
312 +## v1.3.1 - 06/06/2012
313 +
314 + * fixed; thumb() alignment and cropping [thomaschaaf]
315 + * added; hint when graphicsmagick is not installed (#62)
316 + * fixed; minify() (#59)
317 +
318 +## v1.3.0 - 04/11/2012
319 +
320 + * added; flatten support [jwarchol]
321 + * added; background support [jwarchol]
322 + * fixed; identify parser error [chriso]
323 +
324 +## v1.2.0 - 03/30/2012
325 +
326 + * added; extent and gravity support [jwarchol]
327 +
328 +## v1.1.0 - 03/15/2012
329 +
330 + * added; filter() support [travisbeck]
331 + * added; density() [travisbeck]
332 + * fixed; permit either width or height in resize [dambalah]
333 + * updated; docs
334 +
335 +## v1.0.5 - 02/15/2012
336 +
337 + * added; strip() support [Math-]
338 + * added; interlace() support [Math-]
339 + * added; setFormat() support [Math-]
340 + * fixed; regexps for image types [Math-]
341 +
342 +## v1.0.4 - 02/09/2012
343 +
344 + * expose utils
345 +
346 +## v1.0.3 - 01/27/2012
347 +
348 + * removed; console.log
349 +
350 +## v1.0.2 - 01/24/2012
351 +
352 + * added; debugging info on parser errors
353 + * fixed; exports.version
354 +
355 +## v1.0.1 - 01/12/2012
356 +
357 + * fixed; use of reserved keyword `super` for node v0.5+
358 +
359 +## v1.0.0 - 01/12/2012
360 +
361 + * added; autoOrient support [kainosnoema] (#21)
362 + * added; orientation support [kainosnoema] (#21)
363 + * fixed; identify parser now properly JSON formats all data output by `gm identify` such as IPTC, GPS, Make, etc (#20)
364 + * added; support for running as imagemagick (#23, #29)
365 + * added; subclassing support; useful for setting default constructor options like one constructor for ImageMagick, the other for GM
366 + * added; more tests
367 + * changed; remove redundant `orientation`, `resolution`, and `filesize` from `this.data` in `indentify()`. Use their uppercase equivalents.
368 +
369 +## v0.6.0 - 12/14/2011
370 +
371 + * added; stream support [kainosnoema] (#22)
372 +
373 +## v0.5.0 - 07/07/2011
374 +
375 + * added; gm#trim() support [lepokle]
376 + * added; gm#inputIs() support
377 + * fixed; 'geometry does not contain image' error: gh-17
378 +
379 +## v0.4.3 - 05/17/2011
380 +
381 + * added; bunch of tests
382 + * fixed; polygon, polyline, bezier drawing bug
383 +
384 +## v0.4.2 - 05/10/2011
385 +
386 + * added; resize options support
387 +
388 +## v0.4.1 - 04/28/2011
389 +
390 + * shell args are now escaped (thanks @visionmedia)
391 + * added; gm.in()
392 + * added; gm.out()
393 + * various refactoring
394 +
395 +## v0.4.0 - 9/21/2010
396 +
397 + * removed deprecated `new` method
398 + * added drawing docs
399 +
400 +## v0.3.2 - 9/06/2010
401 +
402 + * new images are now created using same gm() constructor
403 +
404 +## v0.3.1 - 9/06/2010
405 +
406 + * can now create images from scratch
407 + * add type method
408 +
409 +## v0.3.0 - 8/26/2010
410 +
411 + * add drawing api
412 +
413 +## v0.2.2 - 8/22/2010
414 +
415 + * add quality option to thumb()
416 + * add teropa to contributors
417 + * added support for colorspace()
418 +
419 +## v0.2.1 - 7/31/2010
420 +
421 + * fixed naming conflict. depth() manipulation method renamed bitdepth()
422 + * added better docs
423 +
424 +## v0.2.0 - 7/29/2010
425 +
426 +new methods
427 +
428 + - swirl
429 + - spread
430 + - solarize
431 + - sharpen
432 + - roll
433 + - sepia
434 + - region
435 + - raise
436 + - lower
437 + - paint
438 + - noise
439 + - negative
440 + - morph
441 + - median
442 + - antialias
443 + - limit
444 + - label
445 + - implode
446 + - gamma
447 + - enhance
448 + - equalize
449 + - emboss
450 + - edge
451 + - dither
452 + - monochrome
453 + - despeckle
454 + - depth
455 + - cycle
456 + - contrast
457 + - comment
458 + - colors
459 +
460 +added more default args to several methods
461 +added more examples
462 +
463 +
464 +## v0.1.2 - 7/28/2010
465 +
466 + * refactor project into separate modules
467 +
468 +
469 +## v0.1.1 - 7/27/2010
470 +
471 + * add modulate method
472 + * add colorize method
473 + * add charcoal method
474 + * add chop method
475 + * bug fix in write without a callback
476 +
477 +
478 +## v0.1.0 - 6/27/2010
479 +
480 + * no longer supporting mogrify
481 + * add image data getter methods
482 +
483 + * size
484 + * format
485 + * color
486 + * res
487 + * depth
488 + * filesize
489 + * identify
490 +
491 + * add new convert methods
492 +
493 + * scale
494 + * resample
495 + * rotate
496 + * flip
497 + * flop
498 + * crop
499 + * magnify
500 + * minify
501 + * quality
502 + * blur
503 + * thumb
504 +
505 +
506 +## v0.0.1 - 6/11/2010
507 +Initial release
1 +
2 +test:
3 + @node test/ --integration $(TESTS)
4 +
5 +test-unit:
6 + @node test/ $(TESTS)
7 +
8 +.PHONY: test test-unit
1 +
2 +# gm v1.17.0 [![Build Status](https://travis-ci.org/aheckmann/gm.png?branch=master)](https://travis-ci.org/aheckmann/gm) [![NPM Version](https://img.shields.io/npm/v/gm.svg?style=flat)](https://www.npmjs.org/package/gm)
3 +
4 +GraphicsMagick and ImageMagick for node
5 +
6 +## Getting started
7 +First download and install [GraphicsMagick](http://www.graphicsmagick.org/) or [ImageMagick](http://www.imagemagick.org/). In Mac OS X, you can simply use [Homebrew](http://mxcl.github.io/homebrew/) and do:
8 +
9 + brew install imagemagick
10 + brew install graphicsmagick
11 +
12 +If you want WebP support with ImageMagick, you must add the WebP option:
13 +
14 + brew install imagemagick --with-webp
15 +
16 +then either use npm:
17 +
18 + npm install gm
19 +
20 +or clone the repo:
21 +
22 + git clone git://github.com/aheckmann/gm.git
23 +
24 +
25 +## Use ImageMagick instead of gm
26 +
27 +Just pass the option `{imageMagick: true}` to enable ImageMagick
28 +
29 +```js
30 +var fs = require('fs')
31 + , gm = require('./gm');
32 +
33 +// resize and remove EXIF profile data
34 +gm('/path/to/my/img.jpg')
35 +.options({imageMagick: true})
36 +.resize(240, 240)
37 +...
38 +```
39 +
40 +
41 +## Basic Usage
42 +
43 +```js
44 +var fs = require('fs')
45 + , gm = require('./gm');
46 +
47 +// resize and remove EXIF profile data
48 +gm('/path/to/my/img.jpg')
49 +.resize(240, 240)
50 +.noProfile()
51 +.write('/path/to/resize.png', function (err) {
52 + if (!err) console.log('done');
53 +});
54 +
55 +// obtain the size of an image
56 +gm('/path/to/my/img.jpg')
57 +.size(function (err, size) {
58 + if (!err)
59 + console.log(size.width > size.height ? 'wider' : 'taller than you');
60 +});
61 +
62 +// output all available image properties
63 +gm('/path/to/img.png')
64 +.identify(function (err, data) {
65 + if (!err) console.log(data)
66 +});
67 +
68 +// pull out the first frame of an animated gif and save as png
69 +gm('/path/to/animated.gif[0]')
70 +.write('/path/to/firstframe.png', function (err) {
71 + if (err) console.log('aaw, shucks');
72 +});
73 +
74 +// auto-orient an image
75 +gm('/path/to/img.jpg')
76 +.autoOrient()
77 +.write('/path/to/oriented.jpg', function (err) {
78 + if (err) ...
79 +})
80 +
81 +// crazytown
82 +gm('/path/to/my/img.jpg')
83 +.flip()
84 +.magnify()
85 +.rotate('green', 45)
86 +.blur(7, 3)
87 +.crop(300, 300, 150, 130)
88 +.edge(3)
89 +.write('/path/to/crazy.jpg', function (err) {
90 + if (!err) console.log('crazytown has arrived');
91 +})
92 +
93 +// annotate an image
94 +gm('/path/to/my/img.jpg')
95 +.stroke("#ffffff")
96 +.drawCircle(10, 10, 20, 10)
97 +.font("Helvetica.ttf", 12)
98 +.drawText(30, 20, "GMagick!")
99 +.write("/path/to/drawing.png", function (err) {
100 + if (!err) console.log('done');
101 +});
102 +
103 +// creating an image
104 +gm(200, 400, "#ddff99f3")
105 +.drawText(10, 50, "from scratch")
106 +.write("/path/to/brandNewImg.jpg", function (err) {
107 + // ...
108 +});
109 +```
110 +
111 +## Streams
112 +
113 +```js
114 +// passing a stream
115 +var readStream = fs.createReadStream('/path/to/my/img.jpg');
116 +gm(readStream, 'img.jpg')
117 +.write('/path/to/reformat.png', function (err) {
118 + if (!err) console.log('done');
119 +});
120 +
121 +// can also stream output to a ReadableStream
122 +// (can be piped to a local file or remote server)
123 +gm('/path/to/my/img.jpg')
124 +.resize('200', '200')
125 +.stream(function (err, stdout, stderr) {
126 + var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
127 + stdout.pipe(writeStream);
128 +});
129 +
130 +// without a callback, .stream() returns a stream
131 +// this is just a convenience wrapper for above.
132 +var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
133 +gm('/path/to/my/img.jpg')
134 +.resize('200', '200')
135 +.stream()
136 +.pipe(writeStream);
137 +
138 +// pass a format or filename to stream() and
139 +// gm will provide image data in that format
140 +gm('/path/to/my/img.jpg')
141 +.stream('png', function (err, stdout, stderr) {
142 + var writeStream = fs.createWriteStream('/path/to/my/reformated.png');
143 + stdout.pipe(writeStream);
144 +});
145 +
146 +// or without the callback
147 +var writeStream = fs.createWriteStream('/path/to/my/reformated.png');
148 +gm('/path/to/my/img.jpg')
149 +.stream('png')
150 +.pipe(writeStream);
151 +
152 +// combine the two for true streaming image processing
153 +var readStream = fs.createReadStream('/path/to/my/img.jpg');
154 +gm(readStream, 'img.jpg')
155 +.resize('200', '200')
156 +.stream(function (err, stdout, stderr) {
157 + var writeStream = fs.createWriteStream('/path/to/my/resized.jpg');
158 + stdout.pipe(writeStream);
159 +});
160 +
161 +// GOTCHA:
162 +// when working with input streams and any 'identify'
163 +// operation (size, format, etc), you must pass "{bufferStream: true}" if
164 +// you also need to convert (write() or stream()) the image afterwards
165 +// NOTE: this buffers the readStream in memory!
166 +var readStream = fs.createReadStream('/path/to/my/img.jpg');
167 +gm(readStream, 'img.jpg')
168 +.size({bufferStream: true}, function(err, size) {
169 + this.resize(size.width / 2, size.height / 2)
170 + this.write('/path/to/resized.jpg', function (err) {
171 + if (!err) console.log('done');
172 + });
173 +});
174 +
175 +```
176 +
177 +## Buffers
178 +
179 +```js
180 +// A buffer can be passed instead of a filepath as well
181 +var buf = require('fs').readFileSync('/path/to/image.jpg');
182 +
183 +gm(buf, 'image.jpg')
184 +.noise('laplacian')
185 +.write('/path/to/out.jpg', function (err) {
186 + if (err) return handle(err);
187 + console.log('Created an image from a Buffer!');
188 +});
189 +
190 +/*
191 +A buffer can also be returned instead of a stream
192 +The first argument to toBuffer is optional, it specifies the image format
193 +*/
194 +gm('img.jpg')
195 +.resize(100, 100)
196 +.toBuffer('PNG',function (err, buffer) {
197 + if (err) return handle(err);
198 + console.log('done!');
199 +})
200 +```
201 +
202 +## Custom Arguments
203 +
204 +If `gm` does not supply you with a method you need or does not work as you'd like, you can simply use `gm().in()` or `gm().out()` to set your own arguments.
205 +
206 +- `gm().command()` - Custom command such as `identify` or `convert`
207 +- `gm().in()` - Custom input arguments
208 +- `gm().out()` - Custom output arguments
209 +
210 +The command will be formatted in the following order:
211 +
212 +1. `command` - ie `convert`
213 +2. `in` - the input arguments
214 +3. `source` - stdin or an image file
215 +4. `out` - the output arguments
216 +5. `output` - stdout or the image file to write to
217 +
218 +For example, suppose you want the following command:
219 +
220 +```bash
221 +gm "convert" "label:Offline" "PNG:-"
222 +```
223 +
224 +However, using `gm().label()` may not work as intended for you:
225 +
226 +```js
227 +gm()
228 +.label('Offline')
229 +.stream();
230 +```
231 +
232 +would yield:
233 +
234 +```bash
235 +gm "convert" "-label" "\"Offline\"" "PNG:-"
236 +```
237 +
238 +Instead, you can use `gm().out()`:
239 +
240 +```js
241 +gm()
242 +.out('label:Offline')
243 +.stream();
244 +```
245 +
246 +which correctly yields:
247 +
248 +```bash
249 +gm "convert" "label:Offline" "PNG:-"
250 +```
251 +
252 +### Custom Identify Format String
253 +
254 +When identifying an image, you may want to use a custom formatting string instead of using `-verbose`, which is quite slow.
255 +You can use your own [formatting string](http://www.imagemagick.org/script/escape.php) when using `gm().identify(format, callback)`.
256 +For example,
257 +
258 +```js
259 +gm('img.png').format(function (err, format) {
260 +
261 +})
262 +
263 +// is equivalent to
264 +
265 +gm('img.png').identify('%m', function (err, format) {
266 +
267 +})
268 +```
269 +
270 +since `%m` is the format option for getting the image file format.
271 +
272 +## Platform differences
273 +
274 +Please document and refer to any [platform or ImageMagick/GraphicsMagick issues/differences here](https://github.com/aheckmann/gm/wiki/GraphicsMagick-and-ImageMagick-versions).
275 +
276 +## Examples:
277 +
278 + Check out the [examples](http://github.com/aheckmann/gm/tree/master/examples/) directory to play around.
279 + Also take a look at the [extending gm](http://wiki.github.com/aheckmann/gm/extending-gm)
280 + page to see how to customize gm to your own needs.
281 +
282 +## Constructor:
283 +
284 + There are a few ways you can use the `gm` image constructor.
285 +
286 + - 1) `gm(path)` When you pass a string as the first argument it is interpreted as the path to an image you intend to manipulate.
287 + - 2) `gm(stream || buffer, [filename])` You may also pass a ReadableStream or Buffer as the first argument, with an optional file name for format inference.
288 + - 3) `gm(width, height, [color])` When you pass two integer arguments, gm will create a new image on the fly with the provided dimensions and an optional background color. And you can still chain just like you do with pre-existing images too. See [here](http://github.com/aheckmann/gm/blob/master/examples/new.js) for an example.
289 +
290 +## Methods
291 +
292 + - getters
293 + - [size](http://aheckmann.github.com/gm/docs.html#getters) - returns the size (WxH) of the image
294 + - [orientation](http://aheckmann.github.com/gm/docs.html#orientation) - returns the EXIF orientation of the image
295 + - [format](http://aheckmann.github.com/gm/docs.html#getters) - returns the image format (gif, jpeg, png, etc)
296 + - [depth](http://aheckmann.github.com/gm/docs.html#getters) - returns the image color depth
297 + - [color](http://aheckmann.github.com/gm/docs.html#getters) - returns the number of colors
298 + - [res](http://aheckmann.github.com/gm/docs.html#getters) - returns the image resolution
299 + - [filesize](http://aheckmann.github.com/gm/docs.html#getters) - returns image filesize
300 + - [identify](http://aheckmann.github.com/gm/docs.html#getters) - returns all image data available. Takes an optional format string.
301 +
302 + - manipulation
303 + - [adjoin](http://aheckmann.github.com/gm/docs.html#adjoin)
304 + - [affine](http://aheckmann.github.com/gm/docs.html#affine)
305 + - [antialias](http://aheckmann.github.com/gm/docs.html#antialias)
306 + - [append](http://aheckmann.github.com/gm/docs.html#append)
307 + - [authenticate](http://aheckmann.github.com/gm/docs.html#authenticate)
308 + - [autoOrient](http://aheckmann.github.com/gm/docs.html#autoOrient)
309 + - [average](http://aheckmann.github.com/gm/docs.html#average)
310 + - [backdrop](http://aheckmann.github.com/gm/docs.html#backdrop)
311 + - [bitdepth](http://aheckmann.github.com/gm/docs.html#bitdepth)
312 + - [blackThreshold](http://aheckmann.github.com/gm/docs.html#blackThreshold)
313 + - [bluePrimary](http://aheckmann.github.com/gm/docs.html#bluePrimary)
314 + - [blur](http://aheckmann.github.com/gm/docs.html#blur)
315 + - [border](http://aheckmann.github.com/gm/docs.html#border)
316 + - [borderColor](http://aheckmann.github.com/gm/docs.html#borderColor)
317 + - [box](http://aheckmann.github.com/gm/docs.html#box)
318 + - [channel](http://aheckmann.github.com/gm/docs.html#channel)
319 + - [charcoal](http://aheckmann.github.com/gm/docs.html#charcoal)
320 + - [chop](http://aheckmann.github.com/gm/docs.html#chop)
321 + - [clip](http://aheckmann.github.com/gm/docs.html#clip)
322 + - [coalesce](http://aheckmann.github.com/gm/docs.html#coalesce)
323 + - [colors](http://aheckmann.github.com/gm/docs.html#colors)
324 + - [colorize](http://aheckmann.github.com/gm/docs.html#colorize)
325 + - [colorMap](http://aheckmann.github.com/gm/docs.html#colorMap)
326 + - [colorspace](http://aheckmann.github.com/gm/docs.html#colorspace)
327 + - [comment](http://aheckmann.github.com/gm/docs.html#comment)
328 + - [compose](http://aheckmann.github.com/gm/docs.html#compose)
329 + - [compress](http://aheckmann.github.com/gm/docs.html#compress)
330 + - [contrast](http://aheckmann.github.com/gm/docs.html#contrast)
331 + - [convolve](http://aheckmann.github.com/gm/docs.html#convolve)
332 + - [createDirectories](http://aheckmann.github.com/gm/docs.html#createDirectories)
333 + - [crop](http://aheckmann.github.com/gm/docs.html#crop)
334 + - [cycle](http://aheckmann.github.com/gm/docs.html#cycle)
335 + - [deconstruct](http://aheckmann.github.com/gm/docs.html#deconstruct)
336 + - [delay](http://aheckmann.github.com/gm/docs.html#delay)
337 + - [define](http://aheckmann.github.com/gm/docs.html#define)
338 + - [density](http://aheckmann.github.com/gm/docs.html#density)
339 + - [despeckle](http://aheckmann.github.com/gm/docs.html#despeckle)
340 + - [dither](http://aheckmann.github.com/gm/docs.html#dither)
341 + - [displace](http://aheckmann.github.com/gm/docs.html#dither)
342 + - [display](http://aheckmann.github.com/gm/docs.html#display)
343 + - [dispose](http://aheckmann.github.com/gm/docs.html#dispose)
344 + - [dissolve](http://aheckmann.github.com/gm/docs.html#dissolve)
345 + - [edge](http://aheckmann.github.com/gm/docs.html#edge)
346 + - [emboss](http://aheckmann.github.com/gm/docs.html#emboss)
347 + - [encoding](http://aheckmann.github.com/gm/docs.html#encoding)
348 + - [enhance](http://aheckmann.github.com/gm/docs.html#enhance)
349 + - [endian](http://aheckmann.github.com/gm/docs.html#endian)
350 + - [equalize](http://aheckmann.github.com/gm/docs.html#equalize)
351 + - [extent](http://aheckmann.github.com/gm/docs.html#extent)
352 + - [file](http://aheckmann.github.com/gm/docs.html#file)
353 + - [filter](http://aheckmann.github.com/gm/docs.html#filter)
354 + - [flatten](http://aheckmann.github.com/gm/docs.html#flatten)
355 + - [flip](http://aheckmann.github.com/gm/docs.html#flip)
356 + - [flop](http://aheckmann.github.com/gm/docs.html#flop)
357 + - [foreground](http://aheckmann.github.com/gm/docs.html#foreground)
358 + - [frame](http://aheckmann.github.com/gm/docs.html#frame)
359 + - [fuzz](http://aheckmann.github.com/gm/docs.html#fuzz)
360 + - [gamma](http://aheckmann.github.com/gm/docs.html#gamma)
361 + - [gaussian](http://aheckmann.github.com/gm/docs.html#gaussian)
362 + - [geometry](http://aheckmann.github.com/gm/docs.html#geometry)
363 + - [gravity](http://aheckmann.github.com/gm/docs.html#gravity)
364 + - [greenPrimary](http://aheckmann.github.com/gm/docs.html#greenPrimary)
365 + - [highlightColor](http://aheckmann.github.com/gm/docs.html#highlightColor)
366 + - [highlightStyle](http://aheckmann.github.com/gm/docs.html#highlightStyle)
367 + - [iconGeometry](http://aheckmann.github.com/gm/docs.html#iconGeometry)
368 + - [implode](http://aheckmann.github.com/gm/docs.html#implode)
369 + - [intent](http://aheckmann.github.com/gm/docs.html#intent)
370 + - [interlace](http://aheckmann.github.com/gm/docs.html#interlace)
371 + - [label](http://aheckmann.github.com/gm/docs.html#label)
372 + - [lat](http://aheckmann.github.com/gm/docs.html#lat)
373 + - [level](http://aheckmann.github.com/gm/docs.html#level)
374 + - [list](http://aheckmann.github.com/gm/docs.html#list)
375 + - [limit](http://aheckmann.github.com/gm/docs.html#limit)
376 + - [log](http://aheckmann.github.com/gm/docs.html#log)
377 + - [loop](http://aheckmann.github.com/gm/docs.html#loop)
378 + - [lower](http://aheckmann.github.com/gm/docs.html#lower)
379 + - [magnify](http://aheckmann.github.com/gm/docs.html#magnify)
380 + - [map](http://aheckmann.github.com/gm/docs.html#map)
381 + - [matte](http://aheckmann.github.com/gm/docs.html#matte)
382 + - [matteColor](http://aheckmann.github.com/gm/docs.html#matteColor)
383 + - [mask](http://aheckmann.github.com/gm/docs.html#mask)
384 + - [maximumError](http://aheckmann.github.com/gm/docs.html#maximumError)
385 + - [median](http://aheckmann.github.com/gm/docs.html#median)
386 + - [minify](http://aheckmann.github.com/gm/docs.html#minify)
387 + - [mode](http://aheckmann.github.com/gm/docs.html#mode)
388 + - [modulate](http://aheckmann.github.com/gm/docs.html#modulate)
389 + - [monitor](http://aheckmann.github.com/gm/docs.html#monitor)
390 + - [monochrome](http://aheckmann.github.com/gm/docs.html#monochrome)
391 + - [morph](http://aheckmann.github.com/gm/docs.html#morph)
392 + - [mosaic](http://aheckmann.github.com/gm/docs.html#mosaic)
393 + - [motionBlur](http://aheckmann.github.com/gm/docs.html#motionBlur)
394 + - [name](http://aheckmann.github.com/gm/docs.html#name)
395 + - [negative](http://aheckmann.github.com/gm/docs.html#negative)
396 + - [noise](http://aheckmann.github.com/gm/docs.html#noise)
397 + - [noop](http://aheckmann.github.com/gm/docs.html#noop)
398 + - [normalize](http://aheckmann.github.com/gm/docs.html#normalize)
399 + - [noProfile](http://aheckmann.github.com/gm/docs.html#profile)
400 + - [opaque](http://aheckmann.github.com/gm/docs.html#opaque)
401 + - [operator](http://aheckmann.github.com/gm/docs.html#operator)
402 + - [orderedDither](http://aheckmann.github.com/gm/docs.html#orderedDither)
403 + - [outputDirectory](http://aheckmann.github.com/gm/docs.html#outputDirectory)
404 + - [paint](http://aheckmann.github.com/gm/docs.html#paint)
405 + - [page](http://aheckmann.github.com/gm/docs.html#page)
406 + - [pause](http://aheckmann.github.com/gm/docs.html#pause)
407 + - [pen](http://aheckmann.github.com/gm/docs.html#pen)
408 + - [ping](http://aheckmann.github.com/gm/docs.html#ping)
409 + - [pointSize](http://aheckmann.github.com/gm/docs.html#pointSize)
410 + - [preview](http://aheckmann.github.com/gm/docs.html#preview)
411 + - [process](http://aheckmann.github.com/gm/docs.html#process)
412 + - [profile](http://aheckmann.github.com/gm/docs.html#profile)
413 + - [progress](http://aheckmann.github.com/gm/docs.html#progress)
414 + - [quality](http://aheckmann.github.com/gm/docs.html#quality)
415 + - [raise](http://aheckmann.github.com/gm/docs.html#raise)
416 + - [rawSize](http://aheckmann.github.com/gm/docs.html#rawSize)
417 + - [randomThreshold](http://aheckmann.github.com/gm/docs.html#randomThreshold)
418 + - [recolor](http://aheckmann.github.com/gm/docs.html#recolor)
419 + - [redPrimary](http://aheckmann.github.com/gm/docs.html#redPrimary)
420 + - [region](http://aheckmann.github.com/gm/docs.html#region)
421 + - [remote](http://aheckmann.github.com/gm/docs.html#remote)
422 + - [render](http://aheckmann.github.com/gm/docs.html#render)
423 + - [repage](http://aheckmann.github.com/gm/docs.html#repage)
424 + - [resample](http://aheckmann.github.com/gm/docs.html#resample)
425 + - [resize](http://aheckmann.github.com/gm/docs.html#resize)
426 + - [roll](http://aheckmann.github.com/gm/docs.html#roll)
427 + - [rotate](http://aheckmann.github.com/gm/docs.html#rotate)
428 + - [sample](http://aheckmann.github.com/gm/docs.html#sample)
429 + - [samplingFactor](http://aheckmann.github.com/gm/docs.html#samplingFactor)
430 + - [scale](http://aheckmann.github.com/gm/docs.html#scale)
431 + - [scene](http://aheckmann.github.com/gm/docs.html#scene)
432 + - [scenes](http://aheckmann.github.com/gm/docs.html#scenes)
433 + - [screen](http://aheckmann.github.com/gm/docs.html#screen)
434 + - [segment](http://aheckmann.github.com/gm/docs.html#segment)
435 + - [sepia](http://aheckmann.github.com/gm/docs.html#sepia)
436 + - [set](http://aheckmann.github.com/gm/docs.html#set)
437 + - [setFormat](http://aheckmann.github.com/gm/docs.html#setformat)
438 + - [shade](http://aheckmann.github.com/gm/docs.html#shade)
439 + - [shadow](http://aheckmann.github.com/gm/docs.html#shadow)
440 + - [sharedMemory](http://aheckmann.github.com/gm/docs.html#sharedMemory)
441 + - [sharpen](http://aheckmann.github.com/gm/docs.html#sharpen)
442 + - [shave](http://aheckmann.github.com/gm/docs.html#shave)
443 + - [shear](http://aheckmann.github.com/gm/docs.html#shear)
444 + - [silent](http://aheckmann.github.com/gm/docs.html#silent)
445 + - [solarize](http://aheckmann.github.com/gm/docs.html#solarize)
446 + - [snaps](http://aheckmann.github.com/gm/docs.html#snaps)
447 + - [stegano](http://aheckmann.github.com/gm/docs.html#stegano)
448 + - [stereo](http://aheckmann.github.com/gm/docs.html#stereo)
449 + - [strip](http://aheckmann.github.com/gm/docs.html#strip) _imagemagick only_
450 + - [spread](http://aheckmann.github.com/gm/docs.html#spread)
451 + - [swirl](http://aheckmann.github.com/gm/docs.html#swirl)
452 + - [textFont](http://aheckmann.github.com/gm/docs.html#textFont)
453 + - [texture](http://aheckmann.github.com/gm/docs.html#texture)
454 + - [threshold](http://aheckmann.github.com/gm/docs.html#threshold)
455 + - [thumb](http://aheckmann.github.com/gm/docs.html#thumb)
456 + - [tile](http://aheckmann.github.com/gm/docs.html#tile)
457 + - [transform](http://aheckmann.github.com/gm/docs.html#transform)
458 + - [transparent](http://aheckmann.github.com/gm/docs.html#transparent)
459 + - [treeDepth](http://aheckmann.github.com/gm/docs.html#treeDepth)
460 + - [trim](http://aheckmann.github.com/gm/docs.html#trim)
461 + - [type](http://aheckmann.github.com/gm/docs.html#type)
462 + - [update](http://aheckmann.github.com/gm/docs.html#update)
463 + - [units](http://aheckmann.github.com/gm/docs.html#units)
464 + - [unsharp](http://aheckmann.github.com/gm/docs.html#unsharp)
465 + - [usePixmap](http://aheckmann.github.com/gm/docs.html#usePixmap)
466 + - [view](http://aheckmann.github.com/gm/docs.html#view)
467 + - [virtualPixel](http://aheckmann.github.com/gm/docs.html#virtualPixel)
468 + - [visual](http://aheckmann.github.com/gm/docs.html#visual)
469 + - [watermark](http://aheckmann.github.com/gm/docs.html#watermark)
470 + - [wave](http://aheckmann.github.com/gm/docs.html#wave)
471 + - [whitePoint](http://aheckmann.github.com/gm/docs.html#whitePoint)
472 + - [whiteThreshold](http://aheckmann.github.com/gm/docs.html#whiteThreshold)
473 + - [window](http://aheckmann.github.com/gm/docs.html#window)
474 + - [windowGroup](http://aheckmann.github.com/gm/docs.html#windowGroup)
475 +
476 + - drawing primitives
477 + - [draw](http://aheckmann.github.com/gm/docs.html#draw)
478 + - [drawArc](http://aheckmann.github.com/gm/docs.html#drawArc)
479 + - [drawBezier](http://aheckmann.github.com/gm/docs.html#drawBezier)
480 + - [drawCircle](http://aheckmann.github.com/gm/docs.html#drawCircle)
481 + - [drawEllipse](http://aheckmann.github.com/gm/docs.html#drawEllipse)
482 + - [drawLine](http://aheckmann.github.com/gm/docs.html#drawLine)
483 + - [drawPoint](http://aheckmann.github.com/gm/docs.html#drawPoint)
484 + - [drawPolygon](http://aheckmann.github.com/gm/docs.html#drawPolygon)
485 + - [drawPolyline](http://aheckmann.github.com/gm/docs.html#drawPolyline)
486 + - [drawRectangle](http://aheckmann.github.com/gm/docs.html#drawRectangle)
487 + - [drawText](http://aheckmann.github.com/gm/docs.html#drawText)
488 + - [fill](http://aheckmann.github.com/gm/docs.html#fill)
489 + - [font](http://aheckmann.github.com/gm/docs.html#font)
490 + - [fontSize](http://aheckmann.github.com/gm/docs.html#fontSize)
491 + - [stroke](http://aheckmann.github.com/gm/docs.html#stroke)
492 + - [strokeWidth](http://aheckmann.github.com/gm/docs.html#strokeWidth)
493 + - [setDraw](http://aheckmann.github.com/gm/docs.html#setDraw)
494 +
495 + - image output
496 + - **write** - writes the processed image data to the specified filename
497 + - **stream** - provides a `ReadableStream` with the processed image data
498 + - **toBuffer** - returns the image as a `Buffer` instead of a stream
499 +
500 +##compare
501 +
502 +Graphicsmagicks `compare` command is exposed through `gm.compare()`. This allows us to determine if two images can be considered "equal".
503 +
504 +Currently `gm.compare` only accepts file paths.
505 +
506 + gm.compare(path1, path2 [, options], callback)
507 +
508 +```js
509 +gm.compare('/path/to/image1.jpg', '/path/to/another.png', function (err, isEqual, equality, raw, path1, path2) {
510 + if (err) return handle(err);
511 +
512 + // if the images were considered equal, `isEqual` will be true, otherwise, false.
513 + console.log('The images were equal: %s', isEqual);
514 +
515 + // to see the total equality returned by graphicsmagick we can inspect the `equality` argument.
516 + console.log('Actual equality: %d', equality);
517 +
518 + // inspect the raw output
519 + console.log(raw);
520 +
521 + // print file paths
522 + console.log(path1, path2);
523 +})
524 +```
525 +
526 +You may wish to pass a custom tolerance threshold to increase or decrease the default level of `0.4`.
527 +
528 +
529 +```js
530 +gm.compare('/path/to/image1.jpg', '/path/to/another.png', 1.2, function (err, isEqual) {
531 + ...
532 +})
533 +```
534 +
535 +To output a diff image, pass a configuration object to define the diff options and tolerance.
536 +
537 +
538 +```js
539 +var options = {
540 + file: '/path/to/diff.png',
541 + highlightColor: 'yellow',
542 + tolerance: 0.02
543 +}
544 +gm.compare('/path/to/image1.jpg', '/path/to/another.png', options, function (err, isEqual, equality, raw) {
545 + ...
546 +})
547 +```
548 +
549 +##composite
550 +
551 +GraphicsMagick supports compositing one image on top of another. This is exposed through `gm.composite()`. Its first argument is an image path with the changes to the base image, and an optional mask image.
552 +
553 +Currently, `gm.composite()` only accepts file paths.
554 +
555 + gm.composite(other [, mask])
556 +
557 +```js
558 +gm('/path/to/image.jpg')
559 +.composite('/path/to/second_image.jpg')
560 +.geometry('+100+150')
561 +.write('/path/to/composite.png', function(err) {
562 + if(!err) console.log("Written composite image.");
563 +});
564 +```
565 +
566 +## Contributors
567 +[https://github.com/aheckmann/gm/contributors](https://github.com/aheckmann/gm/contributors)
568 +
569 +## Inspiration
570 +http://github.com/quiiver/magickal-node
571 +
572 +## Plugins
573 +[https://github.com/aheckmann/gm/wiki](https://github.com/aheckmann/gm/wiki)
574 +
575 +## License
576 +
577 +(The MIT License)
578 +
579 +Copyright (c) 2010 [Aaron Heckmann](aaron.heckmann+github@gmail.com)
580 +
581 +Permission is hereby granted, free of charge, to any person obtaining
582 +a copy of this software and associated documentation files (the
583 +'Software'), to deal in the Software without restriction, including
584 +without limitation the rights to use, copy, modify, merge, publish,
585 +distribute, sublicense, and/or sell copies of the Software, and to
586 +permit persons to whom the Software is furnished to do so, subject to
587 +the following conditions:
588 +
589 +The above copyright notice and this permission notice shall be
590 +included in all copies or substantial portions of the Software.
591 +
592 +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
593 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
594 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
595 +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
596 +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
597 +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
598 +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1 +
2 +/**
3 + * Module dependencies.
4 + */
5 +
6 +var Stream = require('stream').Stream;
7 +var EventEmitter = require('events').EventEmitter;
8 +var util = require('util');
9 +
10 +util.inherits(gm, EventEmitter);
11 +
12 +/**
13 + * Constructor.
14 + *
15 + * @param {String|Number} path - path to img source or ReadableStream or width of img to create
16 + * @param {Number} [height] - optional filename of ReadableStream or height of img to create
17 + * @param {String} [color] - optional hex background color of created img
18 + */
19 +
20 +function gm (source, height, color) {
21 + var width;
22 +
23 + if (!(this instanceof gm)) {
24 + return new gm(source, height, color);
25 + }
26 +
27 + EventEmitter.call(this);
28 +
29 + this._options = {};
30 + this.options(this.__proto__._options);
31 +
32 + this.data = {};
33 + this._in = [];
34 + this._out = [];
35 + this._outputFormat = null;
36 + this._subCommand = 'convert';
37 +
38 + if (source instanceof Stream) {
39 + this.sourceStream = source;
40 + source = height || 'unknown.jpg';
41 + } else if (Buffer.isBuffer(source)) {
42 + this.sourceBuffer = source;
43 + source = height || 'unknown.jpg';
44 + } else if (height) {
45 + // new images
46 + width = source;
47 + source = "";
48 +
49 + this.in("-size", width + "x" + height);
50 +
51 + if (color) {
52 + this.in("xc:"+ color);
53 + }
54 + }
55 +
56 + if (typeof source === "string") {
57 + // then source is a path
58 +
59 + // parse out gif frame brackets from filename
60 + // since stream doesn't use source path
61 + // eg. "filename.gif[0]"
62 + var frames = source.match(/(\[.+\])$/);
63 + if (frames) {
64 + this.sourceFrames = source.substr(frames.index, frames[0].length);
65 + source = source.substr(0, frames.index);
66 + }
67 + }
68 +
69 + this.source = source;
70 +
71 + this.addSrcFormatter(function (src) {
72 + // must be first source formatter
73 +
74 + var inputFromStdin = this.sourceStream || this.sourceBuffer;
75 + var ret = inputFromStdin ? '-' : this.source;
76 +
77 + if (ret && this.sourceFrames) ret += this.sourceFrames;
78 +
79 + src.length = 0;
80 + src[0] = ret;
81 + });
82 +}
83 +
84 +/**
85 + * Subclasses the gm constructor with custom options.
86 + *
87 + * @param {options} options
88 + * @return {gm} the subclasses gm constructor
89 + */
90 +
91 +var parent = gm;
92 +gm.subClass = function subClass (options) {
93 + function gm (source, height, color) {
94 + if (!(this instanceof parent)) {
95 + return new gm(source, height, color);
96 + }
97 +
98 + parent.call(this, source, height, color);
99 + }
100 +
101 + gm.prototype.__proto__ = parent.prototype;
102 + gm.prototype._options = {};
103 + gm.prototype.options(options);
104 +
105 + return gm;
106 +}
107 +
108 +/**
109 + * Augment the prototype.
110 + */
111 +
112 +require("./lib/options")(gm.prototype);
113 +require("./lib/getters")(gm);
114 +require("./lib/args")(gm.prototype);
115 +require("./lib/drawing")(gm.prototype);
116 +require("./lib/convenience")(gm.prototype);
117 +require("./lib/command")(gm.prototype);
118 +require("./lib/compare")(gm.prototype);
119 +require("./lib/composite")(gm.prototype);
120 +
121 +/**
122 + * Expose.
123 + */
124 +
125 +module.exports = exports = gm;
126 +module.exports.utils = require('./lib/utils');
127 +module.exports.compare = require('./lib/compare')();
128 +module.exports.version = JSON.parse(
129 + require('fs').readFileSync(__dirname + '/package.json', 'utf8')
130 +).version;
131 +
1 +# Compiled source #
2 +###################
3 +*.com
4 +*.class
5 +*.dll
6 +*.exe
7 +*.o
8 +*.so
9 +
10 +# Packages #
11 +############
12 +# it's better to unpack these files and commit the raw source
13 +# git has its own built in compression methods
14 +*.7z
15 +*.dmg
16 +*.gz
17 +*.iso
18 +*.jar
19 +*.rar
20 +*.tar
21 +*.zip
22 +
23 +# Logs and databases #
24 +######################
25 +*.log
26 +*.sql
27 +*.sqlite
28 +
29 +# OS generated files #
30 +######################
31 +.DS_Store*
32 +ehthumbs.db
33 +Icon?
34 +Thumbs.db
35 +
36 +# Node.js #
37 +###########
38 +lib-cov
39 +*.seed
40 +*.log
41 +*.csv
42 +*.dat
43 +*.out
44 +*.pid
45 +*.gz
46 +
47 +pids
48 +logs
49 +results
50 +
51 +node_modules
52 +npm-debug.log
53 +
54 +# Components #
55 +##############
56 +
57 +/build
58 +/components
...\ No newline at end of file ...\ No newline at end of file
1 +language: node_js
2 +node_js:
3 + - "0.4"
4 + - "0.6"
5 + - "0.8"
6 + - "0.10"
...\ No newline at end of file ...\ No newline at end of file
1 +# Array Series [![Build Status](https://travis-ci.org/component/array-parallel.png)](https://travis-ci.org/component/array-parallel)
2 +
3 +Call an array of asynchronous functions in parallel
4 +
5 +### API
6 +
7 +#### parallel(fns[, context[, callback]])
8 +
9 +```js
10 +var parallel = require('array-parallel')
11 +
12 +parallel([
13 + function (done) {
14 + done()
15 + }
16 +], this, function (err) {
17 +
18 +})
19 +```
20 +
21 +#### fns
22 +
23 +`fns` is an array of functions to call in parallel.
24 +The argument signature should be:
25 +
26 +```js
27 +function (done) {
28 + done(new Error())
29 + // or
30 + done(null, result)
31 +}
32 +```
33 +
34 +That is, each function should only take a `done` as an argument.
35 +Each callback should only take an `Error` as the first argument,
36 +or a value as the second.
37 +
38 +#### context
39 +
40 +Optional context to pass to each `fn`.
41 +Basically `fn.call(context, done)`.
42 +
43 +#### callback(err, results)
44 +
45 +```js
46 +function (err, results) {
47 +
48 +}
49 +```
50 +
51 +Only argument is an `Error` argument.
52 +It will be the first error retrieved from all the `fns`.
53 +`results` will be an array of results from each `fn`,
54 +thus this could be considered an asynchronous version of `[].map`.
55 +
56 +### License
57 +
58 +The MIT License (MIT)
59 +
60 +Copyright (c) 2013 Jonathan Ong me@jongleberry.com
61 +
62 +Permission is hereby granted, free of charge, to any person obtaining a copy
63 +of this software and associated documentation files (the "Software"), to deal
64 +in the Software without restriction, including without limitation the rights
65 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
66 +copies of the Software, and to permit persons to whom the Software is
67 +furnished to do so, subject to the following conditions:
68 +
69 +The above copyright notice and this permission notice shall be included in
70 +all copies or substantial portions of the Software.
71 +
72 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
73 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
74 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
75 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
76 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
77 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
78 +THE SOFTWARE.
1 +{
2 + "name": "array-parallel",
3 + "description": "Call an array of asynchronous functions in parallel",
4 + "repo": "array-parallel",
5 + "version": "0.1.3",
6 + "main": "index.js",
7 + "scripts": [
8 + "index.js"
9 + ],
10 + "license": "MIT"
11 +}
...\ No newline at end of file ...\ No newline at end of file
1 +module.exports = function parallel(fns, context, callback) {
2 + if (!callback) {
3 + if (typeof context === 'function') {
4 + callback = context
5 + context = null
6 + } else {
7 + callback = noop
8 + }
9 + }
10 +
11 + var pending = fns && fns.length
12 + if (!pending) return callback(null, []);
13 +
14 + var finished = false
15 + var results = new Array(pending)
16 +
17 + fns.forEach(context ? function (fn, i) {
18 + fn.call(context, maybeDone(i))
19 + } : function (fn, i) {
20 + fn(maybeDone(i))
21 + })
22 +
23 + function maybeDone(i) {
24 + return function (err, result) {
25 + if (finished) return;
26 +
27 + if (err) {
28 + callback(err, results)
29 + finished = true
30 + return
31 + }
32 +
33 + results[i] = result
34 +
35 + if (!--pending) callback(null, results);
36 + }
37 + }
38 +}
39 +
40 +function noop() {}
1 +{
2 + "name": "array-parallel",
3 + "description": "Call an array of asynchronous functions in parallel",
4 + "version": "0.1.3",
5 + "scripts": {
6 + "test": "node test"
7 + },
8 + "author": {
9 + "name": "Jonathan Ong",
10 + "email": "me@jongleberry.com",
11 + "url": "http://jongleberry.com"
12 + },
13 + "repository": {
14 + "type": "git",
15 + "url": "https://github.com/component/array-parallel.git"
16 + },
17 + "bugs": {
18 + "url": "https://github.com/component/array-parallel/issues",
19 + "email": "me@jongleberry.com"
20 + },
21 + "license": "MIT",
22 + "homepage": "https://github.com/component/array-parallel",
23 + "_id": "array-parallel@0.1.3",
24 + "dist": {
25 + "shasum": "8f785308926ed5aa478c47e64d1b334b6c0c947d",
26 + "tarball": "http://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz"
27 + },
28 + "_from": "array-parallel@~0.1.0",
29 + "_npmVersion": "1.3.17",
30 + "_npmUser": {
31 + "name": "jongleberry",
32 + "email": "jonathanrichardong@gmail.com"
33 + },
34 + "maintainers": [
35 + {
36 + "name": "jongleberry",
37 + "email": "jonathanrichardong@gmail.com"
38 + }
39 + ],
40 + "directories": {},
41 + "_shasum": "8f785308926ed5aa478c47e64d1b334b6c0c947d",
42 + "_resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz"
43 +}
1 +var assert = require('assert')
2 +var parallel = require('./')
3 +
4 +var a, b, c
5 +parallel([
6 + function (done) {
7 + setTimeout(function () {
8 + done(null, a = 0)
9 + }, 5)
10 + },
11 + function (done) {
12 + setTimeout(function () {
13 + done(null, b = 1)
14 + }, 10)
15 + },
16 + function (done) {
17 + setTimeout(function () {
18 + done(null, c = 2)
19 + }, 15)
20 + }
21 +], function (err, results) {
22 + assert.equal(a, 0)
23 + assert.equal(b, 1)
24 + assert.equal(c, 2)
25 +
26 + assert.deepEqual(results, [0, 1, 2])
27 +})
28 +
29 +var d, e
30 +parallel([
31 + function (done) {
32 + setTimeout(function () {
33 + d = 1
34 + done(new Error('message'))
35 + }, 5)
36 + },
37 + function (done) {
38 + setTimeout(function () {
39 + e = 2
40 + done()
41 + }, 10)
42 + }
43 +], function (err) {
44 + assert.equal(err.message, 'message')
45 + assert.equal(d, 1)
46 + assert.equal(e, undefined)
47 +})
48 +
49 +var context = 'hello'
50 +parallel([function (done) {
51 + assert.equal(this, context)
52 +}], context)
53 +
54 +var f
55 +parallel([function (done) {
56 + f = true
57 + done()
58 +}])
59 +
60 +process.nextTick(function () {
61 + assert.equal(f, true)
62 +})
...\ No newline at end of file ...\ No newline at end of file
1 +# Compiled source #
2 +###################
3 +*.com
4 +*.class
5 +*.dll
6 +*.exe
7 +*.o
8 +*.so
9 +
10 +# Packages #
11 +############
12 +# it's better to unpack these files and commit the raw source
13 +# git has its own built in compression methods
14 +*.7z
15 +*.dmg
16 +*.gz
17 +*.iso
18 +*.jar
19 +*.rar
20 +*.tar
21 +*.zip
22 +
23 +# Logs and databases #
24 +######################
25 +*.log
26 +*.sql
27 +*.sqlite
28 +
29 +# OS generated files #
30 +######################
31 +.DS_Store*
32 +ehthumbs.db
33 +Icon?
34 +Thumbs.db
35 +
36 +# Node.js #
37 +###########
38 +lib-cov
39 +*.seed
40 +*.log
41 +*.csv
42 +*.dat
43 +*.out
44 +*.pid
45 +*.gz
46 +
47 +pids
48 +logs
49 +results
50 +
51 +node_modules
52 +npm-debug.log
53 +
54 +# Components #
55 +##############
56 +
57 +/build
58 +/components
...\ No newline at end of file ...\ No newline at end of file
1 +language: node_js
2 +node_js:
3 + - "0.8"
4 + - "0.10"
...\ No newline at end of file ...\ No newline at end of file
1 +# Array Series [![Build Status](https://travis-ci.org/component/array-series.png)](https://travis-ci.org/component/array-series)
2 +
3 +Call an array of asynchronous functions in series
4 +
5 +### API
6 +
7 +#### series(fns[, context[, callback]])
8 +
9 +```js
10 +var series = require('array-series')
11 +
12 +series([
13 + function (done) {
14 + done()
15 + }
16 +], this, function (err) {
17 +
18 +})
19 +```
20 +
21 +#### fns
22 +
23 +`fns` is an array of functions to call in series.
24 +The argument signature should be:
25 +
26 +```js
27 +function (done) {
28 + done(new Error())
29 + // or
30 + done()
31 +}
32 +```
33 +
34 +That is, each function should only take a `done` as an argument.
35 +Each callback should only take an optional `Error` as an argument.
36 +
37 +#### context
38 +
39 +Optional context to pass to each `fn`.
40 +Basically `fn.call(context, done)`.
41 +
42 +#### callback(err)
43 +
44 +```js
45 +function (err) {
46 +
47 +}
48 +```
49 +
50 +Only argument is an `Error` argument.
51 +It will return the first error in the series of functions that returns an error,
52 +and no function after will be called.
53 +
54 +### License
55 +
56 +The MIT License (MIT)
57 +
58 +Copyright (c) 2013 Jonathan Ong me@jongleberry.com
59 +
60 +Permission is hereby granted, free of charge, to any person obtaining a copy
61 +of this software and associated documentation files (the "Software"), to deal
62 +in the Software without restriction, including without limitation the rights
63 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
64 +copies of the Software, and to permit persons to whom the Software is
65 +furnished to do so, subject to the following conditions:
66 +
67 +The above copyright notice and this permission notice shall be included in
68 +all copies or substantial portions of the Software.
69 +
70 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
73 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
74 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
75 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
76 +THE SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "array-series",
3 + "description": "Call an array of asynchronous functions in series",
4 + "repo": "component/array-series",
5 + "version": "0.1.5",
6 + "main": "index.js",
7 + "scripts": [
8 + "index.js"
9 + ],
10 + "license": "MIT"
11 +}
...\ No newline at end of file ...\ No newline at end of file
1 +module.exports = function series(fns, context, callback) {
2 + if (!callback) {
3 + if (typeof context === 'function') {
4 + callback = context
5 + context = null
6 + } else {
7 + callback = noop
8 + }
9 + }
10 +
11 + if (!(fns && fns.length)) return callback();
12 +
13 + fns = fns.slice(0)
14 +
15 + var call = context
16 + ? function () {
17 + fns.length
18 + ? fns.shift().call(context, next)
19 + : callback()
20 + }
21 + : function () {
22 + fns.length
23 + ? fns.shift()(next)
24 + : callback()
25 + }
26 +
27 + call()
28 +
29 + function next(err) {
30 + err ? callback(err) : call()
31 + }
32 +}
33 +
34 +function noop() {}
1 +{
2 + "name": "array-series",
3 + "description": "Call an array of asynchronous functions in series",
4 + "version": "0.1.5",
5 + "scripts": {
6 + "test": "node test"
7 + },
8 + "author": {
9 + "name": "Jonathan Ong",
10 + "email": "me@jongleberry.com",
11 + "url": "http://jongleberry.com"
12 + },
13 + "repository": {
14 + "type": "git",
15 + "url": "https://github.com/component/array-series.git"
16 + },
17 + "bugs": {
18 + "url": "https://github.com/component/array-series/issues",
19 + "email": "me@jongleberry.com"
20 + },
21 + "license": "MIT",
22 + "homepage": "https://github.com/component/array-series",
23 + "_id": "array-series@0.1.5",
24 + "dist": {
25 + "shasum": "df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f",
26 + "tarball": "http://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz"
27 + },
28 + "_from": "array-series@~0.1.0",
29 + "_npmVersion": "1.3.17",
30 + "_npmUser": {
31 + "name": "jongleberry",
32 + "email": "jonathanrichardong@gmail.com"
33 + },
34 + "maintainers": [
35 + {
36 + "name": "jongleberry",
37 + "email": "jonathanrichardong@gmail.com"
38 + }
39 + ],
40 + "directories": {},
41 + "_shasum": "df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f",
42 + "_resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz"
43 +}
1 +var assert = require('assert')
2 +var series = require('./')
3 +
4 +var a, b, c
5 +
6 +series([
7 + function (done) {
8 + a = 1
9 + process.nextTick(done)
10 + check('a')
11 + },
12 + function (done) {
13 + b = 2
14 + process.nextTick(done)
15 + check('b')
16 + },
17 + function (done) {
18 + c = 3
19 + process.nextTick(done)
20 + check('c')
21 + }
22 +], function (err) {
23 + assert.ifError(err)
24 + assert.equal(a, 1)
25 + assert.equal(b, 2)
26 + assert.equal(c, 3)
27 +})
28 +
29 +function check(x) {
30 + switch (x) {
31 + case 'a':
32 + assert.equal(a, 1)
33 + assert.equal(b, undefined)
34 + assert.equal(c, undefined)
35 + break
36 + case 'b':
37 + assert.equal(a, 1)
38 + assert.equal(b, 2)
39 + assert.equal(c, undefined)
40 + break
41 + case 'c':
42 + assert.equal(a, 1)
43 + assert.equal(b, 2)
44 + assert.equal(c, 3)
45 + break
46 + }
47 +}
48 +
49 +var context = 'hello'
50 +series([function (done) {
51 + assert.equal(this, context)
52 + done()
53 +}], context)
54 +
55 +var finished
56 +series([], function (err) {
57 + finished = true
58 +})
59 +
60 +process.nextTick(function () {
61 + if (!finished)
62 + throw new Error('Failed with no functions.');
63 +})
64 +
65 +var r, d, o
66 +series([
67 + function (done) {
68 + r = 1
69 + process.nextTick(done)
70 + },
71 + function (done) {
72 + d = 0
73 + process.nextTick(function () {
74 + done(new Error('message'))
75 + })
76 + },
77 + function (done) {
78 + o = 0
79 + process.nextTick(done)
80 + }
81 +], function (err) {
82 + assert.equal(err.message, 'message')
83 + assert.equal(r, 1)
84 + assert.equal(d, 0)
85 + assert.equal(o, undefined)
86 +})
87 +
88 +console.log('Array series tests pass!')
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +0.7.0 / 2012-05-04
3 +==================
4 +
5 + * Added .component to package.json
6 + * Added debug.component.js build
7 +
8 +0.6.0 / 2012-03-16
9 +==================
10 +
11 + * Added support for "-" prefix in DEBUG [Vinay Pulim]
12 + * Added `.enabled` flag to the node version [TooTallNate]
13 +
14 +0.5.0 / 2012-02-02
15 +==================
16 +
17 + * Added: humanize diffs. Closes #8
18 + * Added `debug.disable()` to the CS variant
19 + * Removed padding. Closes #10
20 + * Fixed: persist client-side variant again. Closes #9
21 +
22 +0.4.0 / 2012-02-01
23 +==================
24 +
25 + * Added browser variant support for older browsers [TooTallNate]
26 + * Added `debug.enable('project:*')` to browser variant [TooTallNate]
27 + * Added padding to diff (moved it to the right)
28 +
29 +0.3.0 / 2012-01-26
30 +==================
31 +
32 + * Added millisecond diff when isatty, otherwise UTC string
33 +
34 +0.2.0 / 2012-01-22
35 +==================
36 +
37 + * Added wildcard support
38 +
39 +0.1.0 / 2011-12-02
40 +==================
41 +
42 + * Added: remove colors unless stderr isatty [TooTallNate]
43 +
44 +0.0.1 / 2010-01-03
45 +==================
46 +
47 + * Initial release
1 +
2 +debug.component.js: head.js debug.js tail.js
3 + cat $^ > $@
4 +
1 +
2 +# debug
3 +
4 + tiny node.js debugging utility.
5 +
6 +## Installation
7 +
8 +```
9 +$ npm install debug
10 +```
11 +
12 +## Example
13 +
14 + This module is modelled after node core's debugging technique, allowing you to enable one or more topic-specific debugging functions, for example core does the following within many modules:
15 +
16 +```js
17 +var debug;
18 +if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) {
19 + debug = function(x) {
20 + var prefix = process.pid + ',' +
21 + (process.env.NODE_WORKER_ID ? 'Worker' : 'Master');
22 + console.error(prefix, x);
23 + };
24 +} else {
25 + debug = function() { };
26 +}
27 +```
28 +
29 + This concept is extremely simple but it works well. With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility.
30 +
31 +Example _app.js_:
32 +
33 +```js
34 +var debug = require('debug')('http')
35 + , http = require('http')
36 + , name = 'My App';
37 +
38 +// fake app
39 +
40 +debug('booting %s', name);
41 +
42 +http.createServer(function(req, res){
43 + debug(req.method + ' ' + req.url);
44 + res.end('hello\n');
45 +}).listen(3000, function(){
46 + debug('listening');
47 +});
48 +
49 +// fake worker of some kind
50 +
51 +require('./worker');
52 +```
53 +
54 +Example _worker.js_:
55 +
56 +```js
57 +var debug = require('debug')('worker');
58 +
59 +setInterval(function(){
60 + debug('doing some work');
61 +}, 1000);
62 +```
63 +
64 + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
65 +
66 + ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
67 +
68 + ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
69 +
70 +## Millisecond diff
71 +
72 + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
73 +
74 + ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
75 +
76 + When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
77 +
78 + ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)
79 +
80 +## Conventions
81 +
82 + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".
83 +
84 +## Wildcards
85 +
86 + The "*" character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
87 +
88 + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=* -connect:*` would include all debuggers except those starting with "connect:".
89 +
90 +## Browser support
91 +
92 + Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`.
93 +
94 +```js
95 +a = debug('worker:a');
96 +b = debug('worker:b');
97 +
98 +setInterval(function(){
99 + a('doing some work');
100 +}, 1000);
101 +
102 +setInterval(function(){
103 + a('doing some work');
104 +}, 1200);
105 +```
106 +
107 +## License
108 +
109 +(The MIT License)
110 +
111 +Copyright (c) 2011 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
112 +
113 +Permission is hereby granted, free of charge, to any person obtaining
114 +a copy of this software and associated documentation files (the
115 +'Software'), to deal in the Software without restriction, including
116 +without limitation the rights to use, copy, modify, merge, publish,
117 +distribute, sublicense, and/or sell copies of the Software, and to
118 +permit persons to whom the Software is furnished to do so, subject to
119 +the following conditions:
120 +
121 +The above copyright notice and this permission notice shall be
122 +included in all copies or substantial portions of the Software.
123 +
124 +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
125 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
126 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
127 +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
128 +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
129 +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
130 +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
1 +;(function(){
2 +
3 +/**
4 + * Create a debugger with the given `name`.
5 + *
6 + * @param {String} name
7 + * @return {Type}
8 + * @api public
9 + */
10 +
11 +function debug(name) {
12 + if (!debug.enabled(name)) return function(){};
13 +
14 + return function(fmt){
15 + var curr = new Date;
16 + var ms = curr - (debug[name] || curr);
17 + debug[name] = curr;
18 +
19 + fmt = name
20 + + ' '
21 + + fmt
22 + + ' +' + debug.humanize(ms);
23 +
24 + // This hackery is required for IE8
25 + // where `console.log` doesn't have 'apply'
26 + window.console
27 + && console.log
28 + && Function.prototype.apply.call(console.log, console, arguments);
29 + }
30 +}
31 +
32 +/**
33 + * The currently active debug mode names.
34 + */
35 +
36 +debug.names = [];
37 +debug.skips = [];
38 +
39 +/**
40 + * Enables a debug mode by name. This can include modes
41 + * separated by a colon and wildcards.
42 + *
43 + * @param {String} name
44 + * @api public
45 + */
46 +
47 +debug.enable = function(name) {
48 + localStorage.debug = name;
49 +
50 + var split = (name || '').split(/[\s,]+/)
51 + , len = split.length;
52 +
53 + for (var i = 0; i < len; i++) {
54 + name = split[i].replace('*', '.*?');
55 + if (name[0] === '-') {
56 + debug.skips.push(new RegExp('^' + name.substr(1) + '$'));
57 + }
58 + else {
59 + debug.names.push(new RegExp('^' + name + '$'));
60 + }
61 + }
62 +};
63 +
64 +/**
65 + * Disable debug output.
66 + *
67 + * @api public
68 + */
69 +
70 +debug.disable = function(){
71 + debug.enable('');
72 +};
73 +
74 +/**
75 + * Humanize the given `ms`.
76 + *
77 + * @param {Number} m
78 + * @return {String}
79 + * @api private
80 + */
81 +
82 +debug.humanize = function(ms) {
83 + var sec = 1000
84 + , min = 60 * 1000
85 + , hour = 60 * min;
86 +
87 + if (ms >= hour) return (ms / hour).toFixed(1) + 'h';
88 + if (ms >= min) return (ms / min).toFixed(1) + 'm';
89 + if (ms >= sec) return (ms / sec | 0) + 's';
90 + return ms + 'ms';
91 +};
92 +
93 +/**
94 + * Returns true if the given mode name is enabled, false otherwise.
95 + *
96 + * @param {String} name
97 + * @return {Boolean}
98 + * @api public
99 + */
100 +
101 +debug.enabled = function(name) {
102 + for (var i = 0, len = debug.skips.length; i < len; i++) {
103 + if (debug.skips[i].test(name)) {
104 + return false;
105 + }
106 + }
107 + for (var i = 0, len = debug.names.length; i < len; i++) {
108 + if (debug.names[i].test(name)) {
109 + return true;
110 + }
111 + }
112 + return false;
113 +};
114 +
115 +// persist
116 +
117 +if (window.localStorage) debug.enable(localStorage.debug);
118 + module.exports = debug;
119 +
120 +})();
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +/**
3 + * Create a debugger with the given `name`.
4 + *
5 + * @param {String} name
6 + * @return {Type}
7 + * @api public
8 + */
9 +
10 +function debug(name) {
11 + if (!debug.enabled(name)) return function(){};
12 +
13 + return function(fmt){
14 + var curr = new Date;
15 + var ms = curr - (debug[name] || curr);
16 + debug[name] = curr;
17 +
18 + fmt = name
19 + + ' '
20 + + fmt
21 + + ' +' + debug.humanize(ms);
22 +
23 + // This hackery is required for IE8
24 + // where `console.log` doesn't have 'apply'
25 + window.console
26 + && console.log
27 + && Function.prototype.apply.call(console.log, console, arguments);
28 + }
29 +}
30 +
31 +/**
32 + * The currently active debug mode names.
33 + */
34 +
35 +debug.names = [];
36 +debug.skips = [];
37 +
38 +/**
39 + * Enables a debug mode by name. This can include modes
40 + * separated by a colon and wildcards.
41 + *
42 + * @param {String} name
43 + * @api public
44 + */
45 +
46 +debug.enable = function(name) {
47 + localStorage.debug = name;
48 +
49 + var split = (name || '').split(/[\s,]+/)
50 + , len = split.length;
51 +
52 + for (var i = 0; i < len; i++) {
53 + name = split[i].replace('*', '.*?');
54 + if (name[0] === '-') {
55 + debug.skips.push(new RegExp('^' + name.substr(1) + '$'));
56 + }
57 + else {
58 + debug.names.push(new RegExp('^' + name + '$'));
59 + }
60 + }
61 +};
62 +
63 +/**
64 + * Disable debug output.
65 + *
66 + * @api public
67 + */
68 +
69 +debug.disable = function(){
70 + debug.enable('');
71 +};
72 +
73 +/**
74 + * Humanize the given `ms`.
75 + *
76 + * @param {Number} m
77 + * @return {String}
78 + * @api private
79 + */
80 +
81 +debug.humanize = function(ms) {
82 + var sec = 1000
83 + , min = 60 * 1000
84 + , hour = 60 * min;
85 +
86 + if (ms >= hour) return (ms / hour).toFixed(1) + 'h';
87 + if (ms >= min) return (ms / min).toFixed(1) + 'm';
88 + if (ms >= sec) return (ms / sec | 0) + 's';
89 + return ms + 'ms';
90 +};
91 +
92 +/**
93 + * Returns true if the given mode name is enabled, false otherwise.
94 + *
95 + * @param {String} name
96 + * @return {Boolean}
97 + * @api public
98 + */
99 +
100 +debug.enabled = function(name) {
101 + for (var i = 0, len = debug.skips.length; i < len; i++) {
102 + if (debug.skips[i].test(name)) {
103 + return false;
104 + }
105 + }
106 + for (var i = 0, len = debug.names.length; i < len; i++) {
107 + if (debug.names[i].test(name)) {
108 + return true;
109 + }
110 + }
111 + return false;
112 +};
113 +
114 +// persist
115 +
116 +if (window.localStorage) debug.enable(localStorage.debug);
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +var debug = require('../')('http')
3 + , http = require('http')
4 + , name = 'My App';
5 +
6 +// fake app
7 +
8 +debug('booting %s', name);
9 +
10 +http.createServer(function(req, res){
11 + debug(req.method + ' ' + req.url);
12 + res.end('hello\n');
13 +}).listen(3000, function(){
14 + debug('listening');
15 +});
16 +
17 +// fake worker of some kind
18 +
19 +require('./worker');
...\ No newline at end of file ...\ No newline at end of file
1 +<html>
2 + <head>
3 + <title>debug()</title>
4 + <script src="../debug.js"></script>
5 + <script>
6 + // type debug.enable('*') in
7 + // the console and refresh :)
8 +
9 + a = debug('worker:a');
10 + b = debug('worker:b');
11 +
12 + setInterval(function(){
13 + a('doing some work');
14 + }, 1000);
15 +
16 + setInterval(function(){
17 + a('doing some work');
18 + }, 1200);
19 + </script>
20 + </head>
21 + <body>
22 +
23 + </body>
24 +</html>
1 +
2 +var debug = {
3 + foo: require('../')('test:foo'),
4 + bar: require('../')('test:bar'),
5 + baz: require('../')('test:baz')
6 +};
7 +
8 +debug.foo('foo')
9 +debug.bar('bar')
10 +debug.baz('baz')
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +// DEBUG=* node example/worker
3 +// DEBUG=worker:* node example/worker
4 +// DEBUG=worker:a node example/worker
5 +// DEBUG=worker:b node example/worker
6 +
7 +var a = require('../')('worker:a')
8 + , b = require('../')('worker:b');
9 +
10 +function work() {
11 + a('doing lots of uninteresting work');
12 + setTimeout(work, Math.random() * 1000);
13 +}
14 +
15 +work();
16 +
17 +function workb() {
18 + b('doing some work');
19 + setTimeout(workb, Math.random() * 2000);
20 +}
21 +
22 +workb();
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +module.exports = require('./lib/debug');
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "debug",
3 + "version": "0.7.0",
4 + "description": "small debugging utility",
5 + "keywords": [
6 + "debug",
7 + "log",
8 + "debugger"
9 + ],
10 + "author": {
11 + "name": "TJ Holowaychuk",
12 + "email": "tj@vision-media.ca"
13 + },
14 + "dependencies": {},
15 + "devDependencies": {
16 + "mocha": "*"
17 + },
18 + "main": "index",
19 + "browserify": "debug.component.js",
20 + "engines": {
21 + "node": "*"
22 + },
23 + "component": {
24 + "scripts": {
25 + "debug": "debug.component.js"
26 + }
27 + },
28 + "_id": "debug@0.7.0",
29 + "dist": {
30 + "shasum": "f5be05ec0434c992d79940e50b2695cfb2e01b08",
31 + "tarball": "http://registry.npmjs.org/debug/-/debug-0.7.0.tgz"
32 + },
33 + "maintainers": [
34 + {
35 + "name": "tjholowaychuk",
36 + "email": "tj@vision-media.ca"
37 + }
38 + ],
39 + "directories": {},
40 + "_shasum": "f5be05ec0434c992d79940e50b2695cfb2e01b08",
41 + "_from": "debug@0.7.0",
42 + "_resolved": "https://registry.npmjs.org/debug/-/debug-0.7.0.tgz"
43 +}
1 +
2 + module.exports = debug;
3 +
4 +})();
...\ No newline at end of file ...\ No newline at end of file
1 +language: node_js
2 +node_js:
3 + - 0.6
4 + - 0.8
5 + - "0.10"
1 +Apache License, Version 2.0
2 +
3 +Copyright (c) 2011 Dominic Tarr
4 +
5 +Licensed under the Apache License, Version 2.0 (the "License");
6 +you may not use this file except in compliance with the License.
7 +You may obtain a copy of the License at
8 +
9 + http://www.apache.org/licenses/LICENSE-2.0
10 +
11 +Unless required by applicable law or agreed to in writing, software
12 +distributed under the License is distributed on an "AS IS" BASIS,
13 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 +See the License for the specific language governing permissions and
15 +limitations under the License.
1 +The MIT License
2 +
3 +Copyright (c) 2011 Dominic Tarr
4 +
5 +Permission is hereby granted, free of charge,
6 +to any person obtaining a copy of this software and
7 +associated documentation files (the "Software"), to
8 +deal in the Software without restriction, including
9 +without limitation the rights to use, copy, modify,
10 +merge, publish, distribute, sublicense, and/or sell
11 +copies of the Software, and to permit persons to whom
12 +the Software is furnished to do so,
13 +subject to the following conditions:
14 +
15 +The above copyright notice and this permission notice
16 +shall be included in all copies or substantial portions of the Software.
17 +
18 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22 +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1 +var Stream = require('stream')
2 +
3 +// through
4 +//
5 +// a stream that does nothing but re-emit the input.
6 +// useful for aggregating a series of changing but not ending streams into one stream)
7 +
8 +exports = module.exports = through
9 +through.through = through
10 +
11 +//create a readable writable stream.
12 +
13 +function through (write, end, opts) {
14 + write = write || function (data) { this.queue(data) }
15 + end = end || function () { this.queue(null) }
16 +
17 + var ended = false, destroyed = false, buffer = [], _ended = false
18 + var stream = new Stream()
19 + stream.readable = stream.writable = true
20 + stream.paused = false
21 +
22 +// stream.autoPause = !(opts && opts.autoPause === false)
23 + stream.autoDestroy = !(opts && opts.autoDestroy === false)
24 +
25 + stream.write = function (data) {
26 + write.call(this, data)
27 + return !stream.paused
28 + }
29 +
30 + function drain() {
31 + while(buffer.length && !stream.paused) {
32 + var data = buffer.shift()
33 + if(null === data)
34 + return stream.emit('end')
35 + else
36 + stream.emit('data', data)
37 + }
38 + }
39 +
40 + stream.queue = stream.push = function (data) {
41 +// console.error(ended)
42 + if(_ended) return stream
43 + if(data == null) _ended = true
44 + buffer.push(data)
45 + drain()
46 + return stream
47 + }
48 +
49 + //this will be registered as the first 'end' listener
50 + //must call destroy next tick, to make sure we're after any
51 + //stream piped from here.
52 + //this is only a problem if end is not emitted synchronously.
53 + //a nicer way to do this is to make sure this is the last listener for 'end'
54 +
55 + stream.on('end', function () {
56 + stream.readable = false
57 + if(!stream.writable && stream.autoDestroy)
58 + process.nextTick(function () {
59 + stream.destroy()
60 + })
61 + })
62 +
63 + function _end () {
64 + stream.writable = false
65 + end.call(stream)
66 + if(!stream.readable && stream.autoDestroy)
67 + stream.destroy()
68 + }
69 +
70 + stream.end = function (data) {
71 + if(ended) return
72 + ended = true
73 + if(arguments.length) stream.write(data)
74 + _end() // will emit or queue
75 + return stream
76 + }
77 +
78 + stream.destroy = function () {
79 + if(destroyed) return
80 + destroyed = true
81 + ended = true
82 + buffer.length = 0
83 + stream.writable = stream.readable = false
84 + stream.emit('close')
85 + return stream
86 + }
87 +
88 + stream.pause = function () {
89 + if(stream.paused) return
90 + stream.paused = true
91 + return stream
92 + }
93 +
94 + stream.resume = function () {
95 + if(stream.paused) {
96 + stream.paused = false
97 + stream.emit('resume')
98 + }
99 + drain()
100 + //may have become paused again,
101 + //as drain emits 'data'.
102 + if(!stream.paused)
103 + stream.emit('drain')
104 + return stream
105 + }
106 + return stream
107 +}
108 +
1 +{
2 + "name": "through",
3 + "version": "2.3.6",
4 + "description": "simplified stream construction",
5 + "main": "index.js",
6 + "scripts": {
7 + "test": "set -e; for t in test/*.js; do node $t; done"
8 + },
9 + "devDependencies": {
10 + "stream-spec": "~0.3.5",
11 + "tape": "~2.3.2",
12 + "from": "~0.1.3"
13 + },
14 + "keywords": [
15 + "stream",
16 + "streams",
17 + "user-streams",
18 + "pipe"
19 + ],
20 + "author": {
21 + "name": "Dominic Tarr",
22 + "email": "dominic.tarr@gmail.com",
23 + "url": "dominictarr.com"
24 + },
25 + "license": "MIT",
26 + "repository": {
27 + "type": "git",
28 + "url": "https://github.com/dominictarr/through.git"
29 + },
30 + "homepage": "http://github.com/dominictarr/through",
31 + "testling": {
32 + "browsers": [
33 + "ie/8..latest",
34 + "ff/15..latest",
35 + "chrome/20..latest",
36 + "safari/5.1..latest"
37 + ],
38 + "files": "test/*.js"
39 + },
40 + "gitHead": "19ed9b7e84efe7c3e3c8be80f29390b1620e13c0",
41 + "bugs": {
42 + "url": "https://github.com/dominictarr/through/issues"
43 + },
44 + "_id": "through@2.3.6",
45 + "_shasum": "26681c0f524671021d4e29df7c36bce2d0ecf2e8",
46 + "_from": "through@~2.3.1",
47 + "_npmVersion": "1.4.26",
48 + "_npmUser": {
49 + "name": "dominictarr",
50 + "email": "dominic.tarr@gmail.com"
51 + },
52 + "maintainers": [
53 + {
54 + "name": "dominictarr",
55 + "email": "dominic.tarr@gmail.com"
56 + }
57 + ],
58 + "dist": {
59 + "shasum": "26681c0f524671021d4e29df7c36bce2d0ecf2e8",
60 + "tarball": "http://registry.npmjs.org/through/-/through-2.3.6.tgz"
61 + },
62 + "directories": {},
63 + "_resolved": "https://registry.npmjs.org/through/-/through-2.3.6.tgz"
64 +}
1 +#through
2 +
3 +[![build status](https://secure.travis-ci.org/dominictarr/through.png)](http://travis-ci.org/dominictarr/through)
4 +[![testling badge](https://ci.testling.com/dominictarr/through.png)](https://ci.testling.com/dominictarr/through)
5 +
6 +Easy way to create a `Stream` that is both `readable` and `writable`.
7 +
8 +* Pass in optional `write` and `end` methods.
9 +* `through` takes care of pause/resume logic if you use `this.queue(data)` instead of `this.emit('data', data)`.
10 +* Use `this.pause()` and `this.resume()` to manage flow.
11 +* Check `this.paused` to see current flow state. (`write` always returns `!this.paused`).
12 +
13 +This function is the basis for most of the synchronous streams in
14 +[event-stream](http://github.com/dominictarr/event-stream).
15 +
16 +``` js
17 +var through = require('through')
18 +
19 +through(function write(data) {
20 + this.queue(data) //data *must* not be null
21 + },
22 + function end () { //optional
23 + this.queue(null)
24 + })
25 +```
26 +
27 +Or, can also be used _without_ buffering on pause, use `this.emit('data', data)`,
28 +and this.emit('end')
29 +
30 +``` js
31 +var through = require('through')
32 +
33 +through(function write(data) {
34 + this.emit('data', data)
35 + //this.pause()
36 + },
37 + function end () { //optional
38 + this.emit('end')
39 + })
40 +```
41 +
42 +## Extended Options
43 +
44 +You will probably not need these 99% of the time.
45 +
46 +### autoDestroy=false
47 +
48 +By default, `through` emits close when the writable
49 +and readable side of the stream has ended.
50 +If that is not desired, set `autoDestroy=false`.
51 +
52 +``` js
53 +var through = require('through')
54 +
55 +//like this
56 +var ts = through(write, end, {autoDestroy: false})
57 +//or like this
58 +var ts = through(write, end)
59 +ts.autoDestroy = false
60 +```
61 +
62 +## License
63 +
64 +MIT / Apache2
1 +var from = require('from')
2 +var through = require('../')
3 +
4 +var tape = require('tape')
5 +
6 +tape('simple async example', function (t) {
7 +
8 + var n = 0, expected = [1,2,3,4,5], actual = []
9 + from(expected)
10 + .pipe(through(function(data) {
11 + this.pause()
12 + n ++
13 + setTimeout(function(){
14 + console.log('pushing data', data)
15 + this.push(data)
16 + this.resume()
17 + }.bind(this), 300)
18 + })).pipe(through(function(data) {
19 + console.log('pushing data second time', data);
20 + this.push(data)
21 + })).on('data', function (d) {
22 + actual.push(d)
23 + }).on('end', function() {
24 + t.deepEqual(actual, expected)
25 + t.end()
26 + })
27 +
28 +})
1 +var test = require('tape')
2 +var through = require('../')
3 +
4 +// must emit end before close.
5 +
6 +test('end before close', function (assert) {
7 + var ts = through()
8 + ts.autoDestroy = false
9 + var ended = false, closed = false
10 +
11 + ts.on('end', function () {
12 + assert.ok(!closed)
13 + ended = true
14 + })
15 + ts.on('close', function () {
16 + assert.ok(ended)
17 + closed = true
18 + })
19 +
20 + ts.write(1)
21 + ts.write(2)
22 + ts.write(3)
23 + ts.end()
24 + assert.ok(ended)
25 + assert.notOk(closed)
26 + ts.destroy()
27 + assert.ok(closed)
28 + assert.end()
29 +})
30 +
1 +var test = require('tape')
2 +var through = require('../')
3 +
4 +// must emit end before close.
5 +
6 +test('buffering', function(assert) {
7 + var ts = through(function (data) {
8 + this.queue(data)
9 + }, function () {
10 + this.queue(null)
11 + })
12 +
13 + var ended = false, actual = []
14 +
15 + ts.on('data', actual.push.bind(actual))
16 + ts.on('end', function () {
17 + ended = true
18 + })
19 +
20 + ts.write(1)
21 + ts.write(2)
22 + ts.write(3)
23 + assert.deepEqual(actual, [1, 2, 3])
24 + ts.pause()
25 + ts.write(4)
26 + ts.write(5)
27 + ts.write(6)
28 + assert.deepEqual(actual, [1, 2, 3])
29 + ts.resume()
30 + assert.deepEqual(actual, [1, 2, 3, 4, 5, 6])
31 + ts.pause()
32 + ts.end()
33 + assert.ok(!ended)
34 + ts.resume()
35 + assert.ok(ended)
36 + assert.end()
37 +})
38 +
39 +test('buffering has data in queue, when ends', function (assert) {
40 +
41 + /*
42 + * If stream ends while paused with data in the queue,
43 + * stream should still emit end after all data is written
44 + * on resume.
45 + */
46 +
47 + var ts = through(function (data) {
48 + this.queue(data)
49 + }, function () {
50 + this.queue(null)
51 + })
52 +
53 + var ended = false, actual = []
54 +
55 + ts.on('data', actual.push.bind(actual))
56 + ts.on('end', function () {
57 + ended = true
58 + })
59 +
60 + ts.pause()
61 + ts.write(1)
62 + ts.write(2)
63 + ts.write(3)
64 + ts.end()
65 + assert.deepEqual(actual, [], 'no data written yet, still paused')
66 + assert.ok(!ended, 'end not emitted yet, still paused')
67 + ts.resume()
68 + assert.deepEqual(actual, [1, 2, 3], 'resumed, all data should be delivered')
69 + assert.ok(ended, 'end should be emitted once all data was delivered')
70 + assert.end();
71 +})
1 +var test = require('tape')
2 +var through = require('../')
3 +
4 +// must emit end before close.
5 +
6 +test('end before close', function (assert) {
7 + var ts = through()
8 + var ended = false, closed = false
9 +
10 + ts.on('end', function () {
11 + assert.ok(!closed)
12 + ended = true
13 + })
14 + ts.on('close', function () {
15 + assert.ok(ended)
16 + closed = true
17 + })
18 +
19 + ts.write(1)
20 + ts.write(2)
21 + ts.write(3)
22 + ts.end()
23 + assert.ok(ended)
24 + assert.ok(closed)
25 + assert.end()
26 +})
27 +
28 +test('end only once', function (t) {
29 +
30 + var ts = through()
31 + var ended = false, closed = false
32 +
33 + ts.on('end', function () {
34 + t.equal(ended, false)
35 + ended = true
36 + })
37 +
38 + ts.queue(null)
39 + ts.queue(null)
40 + ts.queue(null)
41 +
42 + ts.resume()
43 +
44 + t.end()
45 +})
1 +
2 +var test = require('tape')
3 +var spec = require('stream-spec')
4 +var through = require('../')
5 +
6 +/*
7 + I'm using these two functions, and not streams and pipe
8 + so there is less to break. if this test fails it must be
9 + the implementation of _through_
10 +*/
11 +
12 +function write(array, stream) {
13 + array = array.slice()
14 + function next() {
15 + while(array.length)
16 + if(stream.write(array.shift()) === false)
17 + return stream.once('drain', next)
18 +
19 + stream.end()
20 + }
21 +
22 + next()
23 +}
24 +
25 +function read(stream, callback) {
26 + var actual = []
27 + stream.on('data', function (data) {
28 + actual.push(data)
29 + })
30 + stream.once('end', function () {
31 + callback(null, actual)
32 + })
33 + stream.once('error', function (err) {
34 + callback(err)
35 + })
36 +}
37 +
38 +test('simple defaults', function(assert) {
39 +
40 + var l = 1000
41 + , expected = []
42 +
43 + while(l--) expected.push(l * Math.random())
44 +
45 + var t = through()
46 + var s = spec(t).through().pausable()
47 +
48 + read(t, function (err, actual) {
49 + assert.ifError(err)
50 + assert.deepEqual(actual, expected)
51 + assert.end()
52 + })
53 +
54 + t.on('close', s.validate)
55 +
56 + write(expected, t)
57 +});
58 +
59 +test('simple functions', function(assert) {
60 +
61 + var l = 1000
62 + , expected = []
63 +
64 + while(l--) expected.push(l * Math.random())
65 +
66 + var t = through(function (data) {
67 + this.emit('data', data*2)
68 + })
69 + var s = spec(t).through().pausable()
70 +
71 +
72 + read(t, function (err, actual) {
73 + assert.ifError(err)
74 + assert.deepEqual(actual, expected.map(function (data) {
75 + return data*2
76 + }))
77 + assert.end()
78 + })
79 +
80 + t.on('close', s.validate)
81 +
82 + write(expected, t)
83 +})
84 +
85 +test('pauses', function(assert) {
86 +
87 + var l = 1000
88 + , expected = []
89 +
90 + while(l--) expected.push(l) //Math.random())
91 +
92 + var t = through()
93 +
94 + var s = spec(t)
95 + .through()
96 + .pausable()
97 +
98 + t.on('data', function () {
99 + if(Math.random() > 0.1) return
100 + t.pause()
101 + process.nextTick(function () {
102 + t.resume()
103 + })
104 + })
105 +
106 + read(t, function (err, actual) {
107 + assert.ifError(err)
108 + assert.deepEqual(actual, expected)
109 + })
110 +
111 + t.on('close', function () {
112 + s.validate()
113 + assert.end()
114 + })
115 +
116 + write(expected, t)
117 +})
1 +{
2 + "name": "gm",
3 + "description": "GraphicsMagick and ImageMagick for node.js",
4 + "version": "1.17.0",
5 + "author": {
6 + "name": "Aaron Heckmann",
7 + "email": "aaron.heckmann+github@gmail.com"
8 + },
9 + "keywords": [
10 + "graphics",
11 + "magick",
12 + "image",
13 + "graphicsmagick",
14 + "imagemagick",
15 + "gm",
16 + "convert",
17 + "identify",
18 + "compare"
19 + ],
20 + "engines": {
21 + "node": ">= 0.8.0"
22 + },
23 + "bugs": {
24 + "url": "http://github.com/aheckmann/gm/issues"
25 + },
26 + "licenses": [
27 + {
28 + "type": "MIT",
29 + "url": "http://www.opensource.org/licenses/mit-license.php"
30 + }
31 + ],
32 + "main": "./index",
33 + "scripts": {
34 + "test": "make test-unit; make test;"
35 + },
36 + "repository": {
37 + "type": "git",
38 + "url": "https://github.com/aheckmann/gm.git"
39 + },
40 + "license": "MIT",
41 + "devDependencies": {
42 + "gleak": "0.4.0",
43 + "async": "~0.2.7"
44 + },
45 + "dependencies": {
46 + "debug": "0.7.0",
47 + "array-series": "~0.1.0",
48 + "array-parallel": "~0.1.0",
49 + "through": "~2.3.1"
50 + },
51 + "gitHead": "2fb65bac7c09ab8b09e87b791b146b82c209076e",
52 + "homepage": "https://github.com/aheckmann/gm",
53 + "_id": "gm@1.17.0",
54 + "_shasum": "27a261e0bdfee3d373d24b5a27bd249057355068",
55 + "_from": "gm@",
56 + "_npmVersion": "1.4.28",
57 + "_npmUser": {
58 + "name": "rwky",
59 + "email": "admin@rwky.net"
60 + },
61 + "maintainers": [
62 + {
63 + "name": "aaron",
64 + "email": "aaron.heckmann+github@gmail.com"
65 + },
66 + {
67 + "name": "jongleberry",
68 + "email": "me@jongleberry.com"
69 + },
70 + {
71 + "name": "rwky",
72 + "email": "admin@rwky.net"
73 + },
74 + {
75 + "name": "fragphace",
76 + "email": "fragphace@gmail.com"
77 + }
78 + ],
79 + "dist": {
80 + "shasum": "27a261e0bdfee3d373d24b5a27bd249057355068",
81 + "tarball": "http://registry.npmjs.org/gm/-/gm-1.17.0.tgz"
82 + },
83 + "directories": {},
84 + "_resolved": "https://registry.npmjs.org/gm/-/gm-1.17.0.tgz"
85 +}
1 +{
2 + "Records":[
3 + {
4 + "eventVersion":"2.0",
5 + "eventSource":"aws:s3",
6 + "awsRegion":"us-east-1",
7 + "eventTime":"1970-01-01T00:00:00.000Z",
8 + "eventName":"ObjectCreated:Put",
9 + "userIdentity":{
10 + "principalId":"AIDAJDPLRKLG7UEXAMPLE"
11 + },
12 + "requestParameters":{
13 + "sourceIPAddress":"127.0.0.1"
14 + },
15 + "responseElements":{
16 + "x-amz-request-id":"C3D13FE58DE4C810",
17 + "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
18 + },
19 + "s3":{
20 + "s3SchemaVersion":"1.0",
21 + "configurationId":"testConfigRule",
22 + "bucket":{
23 + "name":"sourcebucket",
24 + "ownerIdentity":{
25 + "principalId":"A3NL1KOZZKExample"
26 + },
27 + "arn":"arn:aws:s3:::sourcebucket"
28 + },
29 + "object":{
30 + "key":"HappyFace.jpg",
31 + "size":1024,
32 + "eTag":"d41d8cd98f00b204e9800998ecf8427e",
33 + "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko"
34 + }
35 + }
36 + }
37 + ]
38 +}
1 +{
2 + "AWSTemplateFormatVersion": "2010-09-09",
3 + "Resources": {
4 + "ExecRole": {
5 + "Type": "AWS::IAM::Role",
6 + "Properties": {
7 + "AssumeRolePolicyDocument": {
8 + "Version" : "2012-10-17",
9 + "Statement": [ {
10 + "Effect": "Allow",
11 + "Principal": {
12 + "Service": [ "lambda.amazonaws.com" ]
13 + },
14 + "Action": [ "sts:AssumeRole" ]
15 + } ]
16 + }
17 + }
18 + },
19 + "ExecRolePolicies": {
20 + "Type": "AWS::IAM::Policy",
21 + "Properties": {
22 + "PolicyName": "ExecRolePolicy",
23 + "PolicyDocument": {
24 + "Version" : "2012-10-17",
25 + "Statement": [ {
26 + "Effect": "Allow",
27 + "Action": [
28 + "logs:*"
29 + ],
30 + "Resource": "arn:aws:logs:*:*:*"
31 + },
32 + {
33 + "Effect": "Allow",
34 + "Action": [
35 + "s3:GetObject",
36 + "s3:PutObject"
37 + ],
38 + "Resource": [
39 + "arn:aws:s3:::*"
40 + ]
41 + } ]
42 + },
43 + "Roles": [ { "Ref": "ExecRole" } ]
44 + }
45 + },
46 + "InvokeRole": {
47 + "Type": "AWS::IAM::Role",
48 + "Properties": {
49 + "AssumeRolePolicyDocument": {
50 + "Version" : "2012-10-17",
51 + "Statement": [ {
52 + "Effect": "Allow",
53 + "Principal": {
54 + "Service": [ "s3.amazonaws.com" ]
55 + },
56 + "Action": [ "sts:AssumeRole" ],
57 + "Condition": {
58 + "ArnLike": {
59 + "sts:ExternalId": "arn:aws:s3:::*"
60 + }
61 + }
62 + } ]
63 + }
64 + }
65 + },
66 + "InvokeRolePolicies": {
67 + "Type": "AWS::IAM::Policy",
68 + "Properties": {
69 + "PolicyName": "ExecRolePolicy",
70 + "PolicyDocument": {
71 + "Version" : "2012-10-17",
72 + "Statement": [
73 + {
74 + "Effect": "Allow",
75 + "Resource": [
76 + "*"
77 + ],
78 + "Action": [
79 + "lambda:InvokeFunction"
80 + ]
81 + }
82 + ]
83 + },
84 + "Roles": [ { "Ref": "InvokeRole" } ]
85 + }
86 + }
87 + }
88 +}