Skip to content
Newer
Older
100644 329 lines (233 sloc) 13.3 KB
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
1 # streamline.js
a3e59a9 @bjouhier first brew
bjouhier authored
2
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
3 `streamline.js` is a language tool to simplify asynchronous Javascript programming.
a3e59a9 @bjouhier first brew
bjouhier authored
4
54cafd6 @bjouhier simplified README
bjouhier authored
5 Instead of writing hairy code like:
d7de0dd @bjouhier Fixed README formatting
bjouhier authored
6
1fe8fcc @bjouhier Edited README.md via GitHub
bjouhier authored
7 ```javascript
99220aa @bjouhier README enhancement + bumped package version
bjouhier authored
8 function archiveOrders(date, cb) {
9 db.connect(function(err, conn) {
10 if (err) return cb(err);
dd66640 @bjouhier reformatted example
bjouhier authored
11 conn.query("select * from orders where date < ?", [date], function(err, orders) {
99220aa @bjouhier README enhancement + bumped package version
bjouhier authored
12 if (err) return cb(err);
13 helper.each(orders, function(order, next) {
dd66640 @bjouhier reformatted example
bjouhier authored
14 conn.execute("insert into archivedOrders ...", [order.id, ...], function(err) {
99220aa @bjouhier README enhancement + bumped package version
bjouhier authored
15 if (err) return cb(err);
dd66640 @bjouhier reformatted example
bjouhier authored
16 conn.execute("delete from orders where id=?", [order.id], function(err) {
99220aa @bjouhier README enhancement + bumped package version
bjouhier authored
17 if (err) return cb(err);
18 next();
19 });
20 });
21 }, function() {
22 console.log("orders have been archived");
23 cb();
24 });
25 });
1fe8fcc @bjouhier Edited README.md via GitHub
bjouhier authored
26 });
27 }
28 ```
1640a95 @bjouhier README edits
bjouhier authored
29
30 you write:
d7de0dd @bjouhier Fixed README formatting
bjouhier authored
31
de13628 @bjouhier Review README.md (introducing github syntax coloring)
bjouhier authored
32 ```javascript
99220aa @bjouhier README enhancement + bumped package version
bjouhier authored
33 function archiveOrders(date, _) {
34 var conn = db.connect(_);
dd66640 @bjouhier reformatted example
bjouhier authored
35 conn.query("select * from orders where date < ?", [date], _).forEach_(_, function(_, order) {
36 conn.execute("insert into archivedOrders ...", [order.id, ...], _);
37 conn.execute("delete from orders where id=?", [order.id], _);
99220aa @bjouhier README enhancement + bumped package version
bjouhier authored
38 });
39 console.log("orders have been archived");
de13628 @bjouhier Review README.md (introducing github syntax coloring)
bjouhier authored
40 }
41 ```
a3e59a9 @bjouhier first brew
bjouhier authored
42
1640a95 @bjouhier README edits
bjouhier authored
43 and streamline transforms the code and takes care of the callbacks!
a3e59a9 @bjouhier first brew
bjouhier authored
44
1640a95 @bjouhier README edits
bjouhier authored
45 No flow control APIs to learn! You just have to follow a simple rule:
a3e59a9 @bjouhier first brew
bjouhier authored
46
1640a95 @bjouhier README edits
bjouhier authored
47 > Replace all callbacks by an underscore and write your code as if all functions were synchronous.
13c5170 @bjouhier simplified README
bjouhier authored
48
1640a95 @bjouhier README edits
bjouhier authored
49 Streamline is not limited to a subset of Javascript.
50 You can use all the features of Javascript in your asynchronous code: conditionals,
51 loops, `try/catch/finally` blocks, anonymous functions, chaining, `this`, etc.
52
53 Streamline also provides _futures_, and asynchronous variants of the EcmaScript 5 array functions (`forEach`, `map`, etc.).
ed80205 @bjouhier Added link to on-line demo
bjouhier authored
54
54cafd6 @bjouhier simplified README
bjouhier authored
55 # Installation
8db09c2 @bjouhier fixed __global initialization bug + added simplify pass to eliminate …
bjouhier authored
56
1640a95 @bjouhier README edits
bjouhier authored
57 NPM, of course:
8db09c2 @bjouhier fixed __global initialization bug + added simplify pass to eliminate …
bjouhier authored
58
bc8436b @bjouhier Edited README.md via GitHub
bjouhier authored
59 ```sh
60 npm install streamline -g
61 ```
c89d6ef @bjouhier introduced compile.loadFileSync and compile.loadFile functions
bjouhier authored
62
32804aa @bjouhier more README edits
bjouhier authored
63 The `-g` option installs streamline _globally_.
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
64 You can also install it _locally_, without `-g` but then the `_node` and `_coffee`
bb42858 @bjouhier Edited README.md via GitHub
bjouhier authored
65 commands will not be in your default PATH.
c89d6ef @bjouhier introduced compile.loadFileSync and compile.loadFile functions
bjouhier authored
66
da49953 @bjouhier documented npm link setup
bjouhier authored
67 Note: If you encounter a permission error when installing on UNIX systems, you should retry with `sudo`.
68
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
69 If you want to use the _fibers_ option (see below), you must also install the fibers library:
3f13d93 @bjouhier small edits to readme
bjouhier authored
70
71 ```sh
691b0ab @bjouhier edited readme
bjouhier authored
72 npm install fibers [-g]
3f13d93 @bjouhier small edits to readme
bjouhier authored
73 ```
691b0ab @bjouhier edited readme
bjouhier authored
74
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
75 # Hello World
691b0ab @bjouhier edited readme
bjouhier authored
76
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
77 Streamline modules have `._js` or `._coffee` extensions and you run them with the `_node` or `_coffee`
78 loader.
691b0ab @bjouhier edited readme
bjouhier authored
79
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
80 Javascripters:
691b0ab @bjouhier edited readme
bjouhier authored
81
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
82 ``` sh
1640a95 @bjouhier README edits
bjouhier authored
83 $ cat > hello._js
84 console.log('hello ...');
85 setTimeout(_, 1000);
86 console.log('... world');
87 ^D
88 $ _node hello
691b0ab @bjouhier edited readme
bjouhier authored
89 ```
90
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
91 Coffeescripters:
a3e59a9 @bjouhier first brew
bjouhier authored
92
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
93 ``` sh
1640a95 @bjouhier README edits
bjouhier authored
94 $ cat > hello._coffee
95 console.log 'hello ...'
96 setTimeout _, 1000
97 console.log '... world'
98 ^D
99 $ _coffee hello
691b0ab @bjouhier edited readme
bjouhier authored
100 ```
c89d6ef @bjouhier introduced compile.loadFileSync and compile.loadFile functions
bjouhier authored
101
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
102 You can also create standalone shell utilities:
c89d6ef @bjouhier introduced compile.loadFileSync and compile.loadFile functions
bjouhier authored
103
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
104 ``` sh
32804aa @bjouhier more README edits
bjouhier authored
105 $ cat > hello.sh
106 #!/usr/bin/env _node
107 console.log('hello ...');
108 setTimeout(_, 1000);
109 console.log('... world');
110 ^D
1640a95 @bjouhier README edits
bjouhier authored
111 $ ./hello.sh
691b0ab @bjouhier edited readme
bjouhier authored
112 ```
c89d6ef @bjouhier introduced compile.loadFileSync and compile.loadFile functions
bjouhier authored
113
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
114 or:
483d5ae @bjouhier documented new method to package streamlined modules (xxx_.js files)
bjouhier authored
115
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
116 ``` sh
32804aa @bjouhier more README edits
bjouhier authored
117 $ cat > hello.sh
118 #!/usr/bin/env _coffee
119 console.log 'hello ...'
120 setTimeout _, 1000
121 console.log '... world'
122 ^D
1640a95 @bjouhier README edits
bjouhier authored
123 $ ./hello.sh
691b0ab @bjouhier edited readme
bjouhier authored
124 ```
125
1640a95 @bjouhier README edits
bjouhier authored
126 # Compiling and writing loaders
127
696dc9e @bjouhier Fixed readme typo
bjouhier authored
128 You can also set up your code so that it can be run directly with `node` or `coffee`.
1640a95 @bjouhier README edits
bjouhier authored
129 You have two options here:
130
32804aa @bjouhier more README edits
bjouhier authored
131 The first one is to compile your source with `_node -c` or `_coffee -c`:
1640a95 @bjouhier README edits
bjouhier authored
132
133 ``` sh
134 $ _node -c .
135 ```
136
137 This command compiles all the `*._js` and `*._coffee` source files in the current directory and its sub-directories. It generates `*.js` files that you can run directly with `node`.
138
139 The second one is to create your own loader with the `register` API. See the [loader example](https://github.com/Sage/streamlinejs/blob/master/examples/loader/loader.md) for details.
140
32804aa @bjouhier more README edits
bjouhier authored
141 Compiling will give you the fastest startup time because node will directly load the compiled `*.js` files but the `register` API has a `cache` option which comes close and the loader saves you a compilation pass.
142
143 # Browser-side use
144
145 You have three options to use streamline in the browser:
146
8cfaa54 @bjouhier fixed #82
bjouhier authored
147 * The first one is to compile the source with `_node -c`. The compiler generates vanilla Javascript code that you can load with `<script>` directives in an HTML page. See the [flows unit test](https://github.com/Sage/streamlinejs/blob/master/test/common/flows-test.html) for an example.
32804aa @bjouhier more README edits
bjouhier authored
148 * You can also transform the code in the browser with the `transform` API. See the [streamlineMe example](https://github.com/Sage/streamlinejs/blob/master/examples/streamlineMe).
149 * A third option is to use the [streamline-require](https://github.com/Sage/streamline-require) infrastructure. This is a very efficient browser-side implementation of `require` that lets you load streamlined modules as well as vanilla Javascript modules in the browser.
691b0ab @bjouhier edited readme
bjouhier authored
150
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
151 # Generation options
691b0ab @bjouhier edited readme
bjouhier authored
152
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
153 Streamline gives you the choice between generating regular callback-based asynchronous code,
b3cce80 @bjouhier documented generators option
bjouhier authored
154 generating code that takes advantage of the [fibers library](https://github.com/laverdet/node-fibers),
155 or generating code for [JavaScript generators](https://developer.mozilla.org/en/New_in_JavaScript_1.7#Generators).
691b0ab @bjouhier edited readme
bjouhier authored
156
32804aa @bjouhier more README edits
bjouhier authored
157 The _callback_ option produces code that does not have any special runtime dependencies.
691b0ab @bjouhier edited readme
bjouhier authored
158
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
159 The _fibers_ option produces simpler code but requires that you install
160 the fibers library (easy: `npm install fibers`).
b3cce80 @bjouhier documented generators option
bjouhier authored
161 This option gives superior development experience: line numbers and comments are preserved in the transformed code;
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
162 you can step with the debugger through asynchronous calls without having to go through complex callbacks, etc.
691b0ab @bjouhier edited readme
bjouhier authored
163
1640a95 @bjouhier README edits
bjouhier authored
164 The _fibers_ option can be activated by passing the `--fibers` option to the `_node` command or by
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
165 setting the `fibers` option when registering streamline
d922c95 @bjouhier API CHANGE: require('streamline') --> require('streamline').register()
bjouhier authored
166 (see the `streamline.register(options)` function.
b3cce80 @bjouhier documented generators option
bjouhier authored
167
168 The _generators_ option is more experimental (but rather solid as it passes all unit tests).
169 It produces code that is similar to the _fibers_ option, although slightly more complex.
170 Like the _fibers_ option, it preserves line numbers and comments.
171 This option does _not_ yet work in node.js because V8 does not currently support generators but it works in Firefox and in [luvmonkey](https://github.com/creationix/luvmonkey).
172 It should work, with minor tweaks, in future versions of V8 that may implement [Harmony generators](http://wiki.ecmascript.org/doku.php?id=harmony:generators).
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
173
174 # Interoperability with standard node.js code
a3e59a9 @bjouhier first brew
bjouhier authored
175
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
176 You can call standard node functions from streamline code. For example the `fs.readFile` function:
a3e59a9 @bjouhier first brew
bjouhier authored
177
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
178 ```javascript
179 function lineCount(path, _) {
180 return fs.readFile(path, "utf8", _).split('\n').length;
181 }
bc8436b @bjouhier Edited README.md via GitHub
bjouhier authored
182 ```
32804aa @bjouhier more README edits
bjouhier authored
183 You can also call streamline functions as if they were standard node functions. For example, the `lineCount` function defined above can be called as follows from non-streamlined modules:
91154a9 @bjouhier integrated into coffeescript + fix for node 0.3.7
bjouhier authored
184
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
185 ```javascript
186 lineCount("README.md", function(err, result) {
187 if (err) return console.error("ERROR: " + err.message);
188 console.log("README has " + result + " lines.");
189 });
190 ```
1640a95 @bjouhier README edits
bjouhier authored
191
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
192 And you can mix streamline functions, classical callback based code and synchrononous functions in the same file.
32804aa @bjouhier more README edits
bjouhier authored
193 Streamline only transforms the functions that have the special `_` parameter.
d7de0dd @bjouhier Fixed README formatting
bjouhier authored
194
b3cce80 @bjouhier documented generators option
bjouhier authored
195 Note: this works with all transformation options.
919bb7e @bjouhier cleanup pass + loader example + README update
bjouhier authored
196 Even if you use the _fibers_ option, you can seamlessly call standard callback based node APIs
197 and the asynchronous functions that you create with streamline have the standard node callback signature.
a3e59a9 @bjouhier first brew
bjouhier authored
198
f8a13b3 @bjouhier progress on the new README
bjouhier authored
199 # Futures
200
201 Streamline provides _futures_, a powerful feature that lets you parallelize I/O operations in a very
202 simple manner.
203
1640a95 @bjouhier README edits
bjouhier authored
204 If you omit the callback (or pass a `null` callback) when calling a streamline function, the function will execute synchronously and return a _future_. The _future_ is just an asynchronous function that you can call later to obtain a result. Here is an example:
f8a13b3 @bjouhier progress on the new README
bjouhier authored
205
206 ```javascript
207 function countLines(path, _) {
208 return fs.readFile(path, "utf8", _).split('\n').length;
209 }
210
211 function compareLineCounts(path1, path2, _) {
212 // parallelize the two countLines operations
213 var n1 = countLines(path1);
214 var n2 = countLines(path2);
1640a95 @bjouhier README edits
bjouhier authored
215 // get the results and diff them
f8a13b3 @bjouhier progress on the new README
bjouhier authored
216 return n1(_) - n2(_);
217 }
218 ```
219
1640a95 @bjouhier README edits
bjouhier authored
220 In this example, `countLines` is called twice without `_` parameter. These calls start the `fs.readFile` asynchronous operations and return immediately two _futures_ (`n1` and `n2`). The `return` statement retrieves the results with `n1(_)` and `n2(_)` calls and computes their difference.
221
222 Futures are very flexible. In the example above, the results are retrieved from the same function, but you can also pass futures to other functions, store them in objects, call them to get the results from a different module, etc. You can also have several readers on the same future.
f8a13b3 @bjouhier progress on the new README
bjouhier authored
223
224 See the [futures](https://github.com/Sage/streamlinejs/wiki/Futures) wiki page for details.
225
9d008fc @bjouhier added an example of parallel loop to the readme
bjouhier authored
226 The [flows module](https://github.com/Sage/streamlinejs/blob/master/lib/util/flows.md) contains utilities to deal with futures:
227 `flows.collect` to wait on an array of futures and `flows.funnel` to limit the number of concurrent operations.
228
32804aa @bjouhier more README edits
bjouhier authored
229 # Asynchronous Array functions
f8a13b3 @bjouhier progress on the new README
bjouhier authored
230
418d2d3 @bjouhier more README edits
bjouhier authored
231 Streamline extends the Array prototype with asynchronous variants of the EcmaScript 5 `forEach`, `map`, `filter`, `reduce`, ... functions. These asynchronous variants are postfixed with an underscore and they take an extra `_` argument (their callback too), but they are otherwise similar to the standard ES5 functions. Here is an example with the `map_` function:
f8a13b3 @bjouhier progress on the new README
bjouhier authored
232
233 ``` javascript
f1443c1 @bjouhier parallel example was silly. Fixed it
bjouhier authored
234 function dirLines(dir, _) {
eaf56f4 @bjouhier fixed glitch in parallel loop example
bjouhier authored
235 return fs.readdir(dir, _).map_(_, function(_, file) {
f1443c1 @bjouhier parallel example was silly. Fixed it
bjouhier authored
236 return fs.readFile(dir + '/' + file, 'utf8', _).split('\n').length;
f8a13b3 @bjouhier progress on the new README
bjouhier authored
237 });
238 }
239 ```
240
9d008fc @bjouhier added an example of parallel loop to the readme
bjouhier authored
241 Parallelizing loops is easy: just pass the number of parallel operations as second argument to the call:
242
243 ``` javascript
f1443c1 @bjouhier parallel example was silly. Fixed it
bjouhier authored
244 function dirLines(dir, _) {
245 // process 8 files in parallel
eaf56f4 @bjouhier fixed glitch in parallel loop example
bjouhier authored
246 return fs.readdir(dir, _).map_(_, 8, function(_, file) {
f1443c1 @bjouhier parallel example was silly. Fixed it
bjouhier authored
247 return fs.readFile(dir + '/' + file, 'utf8', _).split('\n').length;
9d008fc @bjouhier added an example of parallel loop to the readme
bjouhier authored
248 });
249 }
250 ```
251
f1443c1 @bjouhier parallel example was silly. Fixed it
bjouhier authored
252 If you don't want to limit the level of parallelism, just pass `-1`.
9d008fc @bjouhier added an example of parallel loop to the readme
bjouhier authored
253
51fc4cd @bjouhier added links to API doc files
bjouhier authored
254 See the documentation of the [builtins module](https://github.com/Sage/streamlinejs/blob/master/lib/compiler/builtins.md) for details.
255
17d4ee8 @bjouhier added exception handling section to readme
bjouhier authored
256 # Exception Handling
257
258 Streamline lets you do your exception handling with the usual `try/catch` construct. The `finally` clause is also supported.
259
260 Streamline overrides the `ex.stack` getter to give you the stack of streamline calls rather than the last callback stack. You can still get the native callback stack trace with `ex.rawStack`.
261
262 Exception handling also works with futures.
263 If a future throws an exception before you try to read its result, the exception will be memorized by the future and you will get it at the point where your try to read the result.
264 For example:
265
266 ``` javascript
267 try {
268 var n1 = countLines(badPath);
269 var n2 = countLines(goodPath);
270 setTimeout(_, 1000); // n1 fails, exception is memorized
271 return n1(_) - n2(_); // exception is thrown by n1(_) expression.
272 } catch (ex) {
fb4394e @bjouhier created FAQ.md
bjouhier authored
273 console.error(ex.stack); // exception caught here
17d4ee8 @bjouhier added exception handling section to readme
bjouhier authored
274 }
275 ```
276
32804aa @bjouhier more README edits
bjouhier authored
277 # Stream Wrappers
f8a13b3 @bjouhier progress on the new README
bjouhier authored
278
51fc4cd @bjouhier added links to API doc files
bjouhier authored
279 Streamline also provides _stream wrappers_ that simplify stream programming. The [streams module](https://github.com/Sage/streamlinejs/blob/master/lib/streams/server/streams.md) contains:
f8a13b3 @bjouhier progress on the new README
bjouhier authored
280
99220aa @bjouhier README enhancement + bumped package version
bjouhier authored
281 * a generic `ReadableStream` wrapper with an asynchronous `stream.read(_[, len])` method.
282 * a generic `WritableStream` wrapper with an asynchronous `stream.write(_, buf[, encoding])` method.
f8a13b3 @bjouhier progress on the new README
bjouhier authored
283 * wrappers for HTTP and TCP request and response objects (client and server).
284
a6c322e @bjouhier more readme edits
bjouhier authored
285 # Examples
286
015646b @bjouhier add link to tutorial in readme
bjouhier authored
287 The [tutorial](https://github.com/Sage/streamlinejs/blob/master/tutorial/tutorial.md) shows streamline.js in action on a simple _search aggregator_ application.
a6c322e @bjouhier more readme edits
bjouhier authored
288
015646b @bjouhier add link to tutorial in readme
bjouhier authored
289 The [diskUsage](https://github.com/Sage/streamlinejs/blob/master/examples/diskUsage) examples show an asynchronous directory traversal that computes disk usage.
a6c322e @bjouhier more readme edits
bjouhier authored
290
ecedfcc @bjouhier fixed typo in readme
bjouhier authored
291 # Online demo
32804aa @bjouhier more README edits
bjouhier authored
292
ecedfcc @bjouhier fixed typo in readme
bjouhier authored
293 You can see how streamline transforms the code by playing with the [online demo](http://sage.github.com/streamlinejs/examples/streamlineMe/streamlineMe.html).
32804aa @bjouhier more README edits
bjouhier authored
294
7e3c904 @bjouhier fixed link in readme
bjouhier authored
295 If you are curious, you can also play with the [generators demo - Firefox only](http://sage.github.com/streamlinejs/examples/streamlineMe/yieldMe.html).
b3cce80 @bjouhier documented generators option
bjouhier authored
296
6601cd0 @bjouhier added link to FAQ in README + improved FAQ
bjouhier authored
297 # Troubleshooting
298
299 Read the [FAQ](https://github.com/Sage/streamlinejs/blob/master/FAQ.md).
300
301 If you don't find your answer in the FAQ, post to the [mailing list](http://groups.google.com/group/streamlinejs), or file an issue in [GitHub's issue tracking](https://github.com/Sage/streamlinejs/issues).
302
f45bd46 @bjouhier reviewed readme
bjouhier authored
303 # Related Packages
304
305 The following packages use streamline.js:
306
307 * [streamline-require](https://github.com/Sage/streamline-require): a light and efficient _require_ infrastructure for modules in the browser.
308 * [streamline-pdfkit](https://github.com/Sage/streamline-pdfkit): a fork of [pdfkit](https://github.com/devongovett/pdfkit) in which all the sync calls have been eliminated.
309 * [streamline-zip](https://github.com/Sage/streamline-zip): a fork of [node-native-zip](https://github.com/janjongboom/node-native-zip) with async deflate.
310
311
32804aa @bjouhier more README edits
bjouhier authored
312 # Resources
a3e59a9 @bjouhier first brew
bjouhier authored
313
015646b @bjouhier add link to tutorial in readme
bjouhier authored
314 The [tutorial](https://github.com/Sage/streamlinejs/blob/master/tutorial/tutorial.md) and [FAQ](https://github.com/Sage/streamlinejs/blob/master/FAQ.md) are must-reads for starters.
418d2d3 @bjouhier more README edits
bjouhier authored
315
015646b @bjouhier add link to tutorial in readme
bjouhier authored
316 The API is documented [here](https://github.com/Sage/streamlinejs/blob/master/API.md).
1b054b7 @bjouhier Edited README.md via GitHub
bjouhier authored
317
6601cd0 @bjouhier added link to FAQ in README + improved FAQ
bjouhier authored
318 For support and discussion, please join the [streamline.js mailing list](http://groups.google.com/group/streamlinejs).
a3e59a9 @bjouhier first brew
bjouhier authored
319
32804aa @bjouhier more README edits
bjouhier authored
320 # Credits
ff558a2 @bjouhier bumped package version and updated readme
bjouhier authored
321
a6c322e @bjouhier more readme edits
bjouhier authored
322 See the [AUTHORS](https://github.com/Sage/streamlinejs/blob/master/AUTHORS) file.
323
ff558a2 @bjouhier bumped package version and updated readme
bjouhier authored
324 Special thanks to Marcel Laverdet who contributed the _fibers_ implementation.
325
32804aa @bjouhier more README edits
bjouhier authored
326 # License
a3e59a9 @bjouhier first brew
bjouhier authored
327
328 This work is licensed under the [MIT license](http://en.wikipedia.org/wiki/MIT_License).
Something went wrong with that request. Please try again.