GithubHelp home page GithubHelp logo

zalando / tailor Goto Github PK

View Code? Open in Web Editor NEW
1.7K 61.0 142.0 755 KB

A streaming layout service for front-end microservices

Home Page: http://www.mosaic9.org

License: MIT License

JavaScript 100.00%
tailor layout-service streams mosaic microservice nodejs fragments web

tailor's Introduction

Tailor

NPM Build Status Test Coverage OpenTracing Badge

npm status

downloads version

Tailor is a layout service that uses streams to compose a web page from fragment services. O'Reilly describes it in the title of this blog post as "a library that provides a middleware which you can integrate into any Node.js server." It's partially inspired by Facebook’s BigPipe, but developed in an ecommerce context.

Some of Tailor's features and benefits:

  • Composes pre-rendered markup on the backend. This is important for SEO and fastens the initial render.
  • Ensures a fast Time to First Byte. Tailor requests fragments in parallel and streams them as soon as possible, without blocking the rest of the page.
  • Enforces performance budget. This is quite challenging otherwise, because there is no single point where you can control performance.
  • Fault Tolerance. Render the meaningful output, even if a page fragment has failed or timed out.

Tailor is part of Project Mosaic, which aims to help developers create microservices for the frontend. The Mosaic also includes an extendable HTTP router for service composition (Skipper) with related RESTful API that stores routes (Innkeeper); more components are in the pipeline for public release. If your front-end team is making the monolith-to-microservices transition, you might find Tailor and its available siblings beneficial.

Why a Layout Service?

Microservices get a lot of traction these days. They allow multiple teams to work independently from each other, choose their own technology stacks and establish their own release cycles. Unfortunately, frontend development hasn’t fully capitalized yet on the benefits that microservices offer. The common practice for building websites remains “the monolith”: a single frontend codebase that consumes multiple APIs.

What if we could have microservices on the frontend? This would allow frontend developers to work together with their backend counterparts on the same feature and independently deploy parts of the website — “fragments” such as Header, Product, and Footer. Bringing microservices to the frontend requires a layout service that composes a website out of fragments. Tailor was developed to solve this need.

Installation

Begin using Tailor with:

yarn add node-tailor
const http = require('http');
const Tailor = require('node-tailor');
const tailor = new Tailor({/* Options */});
const server = http.createServer(tailor.requestHandler);
server.listen(process.env.PORT || 8080);

Options

  • fetchContext(request) - Function that returns a promise of the context, that is an object that maps fragment id to fragment url, to be able to override urls of the fragments on the page, defaults to Promise.resolve({})
  • fetchTemplate(request, parseTemplate) - Function that should fetch the template, call parseTemplate and return a promise of the result. Useful to implement your own way to retrieve and cache the templates, e.g. from s3. Default implementation lib/fetch-template.js fetches the template from the file system
  • templatesPath - To specify the path where the templates are stored locally, Defaults to /templates/
  • fragmentTag - Name of the fragment tag, defaults to fragment
  • handledTags - An array of custom tags, check tests/handle-tag for more info
  • handleTag(request, tag, options, context) - Receives a tag or closing tag and serializes it to a string or returns a stream
  • filterRequestHeaders(attributes, request) - Function that filters the request headers that are passed to fragment request, check default implementation in lib/filter-headers
  • filterResponseHeaders(attributes, headers) - Function that maps the given response headers from the primary fragment request to the final response
  • maxAssetLinks - Number of Link Header directives for CSS and JS respected per fragment - defaults to 1
  • requestFragment(filterHeaders)(url, attributes, request) - Function that returns a promise of request to a fragment server, check the default implementation in lib/request-fragment
  • amdLoaderUrl - URL to AMD loader. We use RequireJS from cdnjs as default
  • pipeInstanceName - Pipe instance name that is available in the browser window for consuming frontend hooks.
  • pipeAttributes(attributes) - Function that returns the minimal set of fragment attributes available on the frontend hooks.
  • tracer - Opentracing compliant Tracer implementation.

Template

Tailor uses parse5 to parse the template, where it replaces each fragmentTag with a stream from the fragment server and handledTags with the result of handleTag function.

<html>
<head>
    <script type="fragment" src="http://assets.domain.com"></script>
</head>
<body>
    <fragment src="http://header.domain.com"></fragment>
    <fragment src="http://content.domain.com" primary></fragment>
    <fragment src="http://footer.domain.com" async></fragment>
</body>
</html>

Fragment attributes

  • id - optional unique identifier (autogenerated)
  • src - URL of the fragment
  • primary - denotes a fragment that sets the response code of the page
  • timeout - optional timeout of fragment in milliseconds (default is 3000)
  • async - postpones the fragment until the end of body tag
  • public - to prevent tailor from forwarding filtered request headers from upstream to the fragments.
  • fallback-src - URL of the fallback fragment in case of timeout/error on the current fragment

Other attributes are allowed and will be passed as well to relevant functions (eg. filterRequestHeaders, filterResponseHeaders, etc.)

Fragment server

A fragment is an http(s) server that renders only the part of the page and sets Link header to provide urls to CSS and JavaScript resources. Check examples/basic-css-and-js/index.js for a draft implementation.

A JavaScript of the fragment is an AMD module, that exports an init function, that will be called with DOM element of the fragment as an argument.

Tailor will not follow redirects even if fragment response contains 'Location' Header, that is on purpose as redirects can introduce unwanted latency. Fragments with the attribute primary can do a redirect since it controls the status code of the page.

Note: For compatability with AWS the Link header can also be passed as x-amz-meta-link

Passing information to fragments

By default, the incoming request will be used to selecting the template.

So to get the index.html template you go to /index.

If you want to listen to /product/my-product-123 to go to product.html template, you can change the req.url to /product.

Every header is filtered by default to avoid leaking information, but you can give the original URI and host by adding it to the headers, x-request-host and x-request-uri, then reading in the fragment the headers to know what product to fetch and display.

http
    .createServer((req, res) => {
        req.headers['x-request-uri'] = req.url
        req.url = '/index'

        tailor.requestHandler(req, res);
    })
    .listen(8080, function() {
        console.log('Tailor server listening on port 8080');
    });

Concepts

Some of the concepts in Tailor are described in detail on the specific docs.

OpenTracing

Tailor has out of the box distributed tracing instrumentation with OpenTracing. It will pick up any span context on the ingress HTTP request and propagate it to the existing Remote Procedure Calls (RPCs).

Currently, only the fetching of fragments is instrumented providing some additional details like the fragment tag, attributes and some logging payload like the stack trace for errors.

Examples

# Get a copy of the repository
git clone https://github.com/zalando/tailor.git

# Change to the folder
cd tailor

# Install dependencies
yarn
  • Basic - node examples/basic
  • CSS and JS - node examples/basic-css-and-js
  • Multiple Fragments and AMD - node examples/multiple-fragments-with-custom-amd
  • Fragment Performance - node examples/fragment-performance

Go to http://localhost:8080/index after running the specific example.

Note: Please run the examples with node versions > 6.0.0


Single-page application examples are also available:

Benchmark

To start running benchmark execute npm run benchmark and wait for couple of seconds to see the results.

Contributing

Please check the Contributing guidelines here.

tailor's People

Contributors

addityasingh avatar arhont375 avatar aryszka avatar cbm-othomas avatar danielbayerlein avatar dependabot[bot] avatar deteam avatar grassator avatar iilei avatar jukey avatar kay-schecker avatar kevinsimper avatar kuzminadya avatar lahdekorpi avatar lappleapple avatar lmineiro avatar luizdesign avatar mariadodevska avatar mo-gr avatar rbarilani avatar rikkert avatar robinwhittleton avatar ruiaraujo avatar shershen08 avatar simeonc avatar timsim00 avatar tsnolan23 avatar vigneshshanmugam avatar w0rm avatar ytanruengsri avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tailor's Issues

Support for web component style fragments

Hi,

really like your project. I was planning to build something very similar but luckily found this :)
I would like to compose the structure of my site out of web components that do their rendering client side. But I want the option to deliver the critical components prerenderd from the server (i.e. through universal js). This part is very similar to your fragment loading, but requires a slightliy different syntax:

<my-component name="alice" prerender></my-component>

This will call the backend with an url like this http://localhost:9000/my/component?name=hello and insert the respone body (<h1>Hello Alice!</h1>) into the component.

<my-component name="alice">
  <h1>Hello Alice!</h1>
</my-component>

To implement this, the following things have to be addressed:

  1. identify fragments by attribute (i.e. prerender)
  2. don't transform the fragment tag into a div
  3. provide a way to translate a tag name and attributes to a url (mapping / resolve function)

Do you think this is something that fits into the project?
I would like to contribute and work on this topics.

\ naltatis

Is maintainers file current?

Wondering if Andrey K's still contributing; also Aditya now seems to be doing a lot of work but you guys would decide if he's "maintainer" status.

Add a fixed set of options to Tailor

Tailor currently offers a set of options and every new feature also starts with introducing a new set of options.

  1. Deprecate the unused/unimportant options
  2. Cluster the options based on fragment/template level (#97)
  3. Moving completely to ES6 will help in reducing the code bloat when considering the default options as well (#102)

This is going to be a breaking change and will be introduced as part of the next major release.

Proposal - Improving the Interactivity of primary content

Since tailor downloads all the scripts from the fragments asynchronously(Not in order) and all these scripts are loaded as AMD modules by require.js, The order of the script execution is not guaranteed since they are async. This introduces problems in some cases

Problem

  • If the primary fragment script size is too big, Sometimes this does happen in real life and you could see others fragments racing with the primary and getting interactive (scripts being executed) even before primary.

Ideal workaround

  • primary fragments can send a initial code chunk and can do code splitting to lazy load their assets to make it interactive ASAP.

Even though its the recommended way of improving the performance of the page, It does need lots of iteration and identify the critical JS parts that are needed by the page.

Preload to the rescue

We could solve this issue by prioritising resources using Preloading techniques.

Tailor does wait for the primary fragment on any given page since the primary fragment decides the Status Code of the Page, We could preload the available assets(JS and CSS) from the fragments before writing the Response Headers of the page.

Working on the Implementation, Will update the metrics here

Improve documentation in readme

Disclaimer: I am not a nodejs / frontend developer, so some steps that are apparent for devs working with such stack might not be obvious for me.

So I cloned the repository, walked into this folder and wanted to start basic examples:

Examples

  • Basic - node examples/basic
    ...
    Go to http://localhost:8080/index after running the specific example.
    Note: Please run the examples with --harmony flag for node 4.x versions

So I've done that:

ylazaryev@ylazaryev ~/p/tailor> node --harmony examples/basic
/home/ylazaryev/projects/tailor/index.js:17
    const { primary, id } = attributes;
          ^

SyntaxError: Unexpected token {
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:374:25)
    at Object.Module._extensions..js (module.js:417:10)
    at Module.load (module.js:344:32)
    at Function.Module._load (module.js:301:12)
    at Module.require (module.js:354:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (/home/ylazaryev/projects/tailor/examples/basic/index.js:5:16)
    at Module._compile (module.js:410:26)
    at Object.Module._extensions..js (module.js:417:10)

Thanks to @ytanruengsri helped me by saying that I am supposed to run node version 7.

This is what I suggest to improve first:
Specify which node versions are supported.

But thats not the end yet:

ylazaryev@ylazaryev ~/p/tailor> node --version
v7.4.0

ylazaryev@ylazaryev ~/p/tailor> node examples/basic
fs.js:558
  return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
                 ^

Error: ENOENT: no such file or directory, open '/home/ylazaryev/projects/tailor/src/pipe.min.js'
    at Object.fs.openSync (fs.js:558:18)
    at Object.fs.readFileSync (fs.js:468:33)
    at Object.<anonymous> (/home/ylazaryev/projects/tailor/index.js:11:28)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)

ylazaryev@ylazaryev ~/p/tailor> npm test

> [email protected] test /home/ylazaryev/projects/tailor
> mocha --harmony tests/**

sh: 1: mocha: not found
npm ERR! Test failed.  See above for more details.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?


ylazaryev@ylazaryev ~/p/tailor> npm i mocha --save

lazaryev@ylazaryev ~/p/tailor> npm test

> [email protected] test /home/ylazaryev/projects/tailor
> mocha --harmony tests/**

module.js:472
    throw err;
    ^

Error: Cannot find module 'nock'
    at Function.Module._resolveFilename (module.js:470:15)
    at Function.Module._load (module.js:418:25)

ylazaryev@ylazaryev ~/p/tailor> npm i nock --save

so after installing 'mocha', 'nock', 'sinon' I have arrived at the same error:

lazaryev@ylazaryev ~/p/tailor> npm test

> [email protected] test /home/ylazaryev/projects/tailor
> mocha --harmony tests/**

fs.js:558
  return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
```                                                  ^

So the next suggestion would be to *document dependencies needed or how to install them*.



Improve documentation

We need a section about template syntax and a section about fragment's attributes.

Questions about Tailor

I'm not sure whether an plain HTTP server (http-module) is the right thing. Do you use the plain HTTP server (http-module) in production?

Works Tailor with @hapijs? @hapijs has many useful modules.

  • Where is the security layer - Fragment or Tailor?
  • Do you have an example for a more complex URL routing with dynamic and static parameters?

A more complex example would be great.

SystemJS instead of AMD?

We should start discussing about this soon since SystemJS supports native modules and supports lots of useful feature like custom hooks/resolvers.

Use with node-http-proxy

Could someone give me an example of how to use a streaming response from node-http-proxy as a template input?

My templates come through node-http-proxy and I ideally need to then pipe this response through to tailor - any thoughts welcomed!

Get rid of ids

Using ids for the fragment identification is unsafe, because the fragment may have such id inside the content.

For the sync fragment it is possible to get the position of the <script> tag and consider the placeholder to be the previous sibling.

For async fragments we still need a link to the placeholder.

Installation fails: "Refusing to install node-tailor as a dependency of itself"

I tried to install from a freshly cloned repository and always getting this error message:

npm i node-tailor --save
npm ERR! Linux 4.4.0-28-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "i" "node-tailor" "--save"
npm ERR! node v4.2.6
npm ERR! npm  v3.5.2
npm ERR! code ENOSELF

npm ERR! Refusing to install node-tailor as a dependency of itself
npm ERR! 
npm ERR! If you need help, you may report this error at:
npm ERR!     <https://github.com/npm/npm/issues>

npm ERR! Please include the following file with any support request:
npm ERR!     /home/jukey/projects/tailor/npm-debug.log

See also:
npm-debug.log.txt

It takes around 10h for Tailor to recognise a new stack with new fragment on AWS

We deployed a new version of a stack with a fragment yesterday around 15:00 (CEST). Right after deployment we routed 100% of traffic to the new stack (senza traffic 100). According to our monitoring data we see that old stack was getting requests around 10h after deployment of new stack. Tailor is only one "clinet" which is calling our stacks.

Green - old stack
Yellow - new stack

screen shot 2016-09-14 at 12 31 13

Required fragments

Hi!
Is there any way to mark some fragments as "required" so if they return error code we should return some general error page instead of broken one? This can be useful in case we want to show "Server error page" if server that server our header & footer is unavailable.

P.S.: It this behaviour makes sense for you guys I can help with a PR.

Tailor example is not working

Tailor started at port 8080
Fragment1 started at port 8081
Fragment2 started at port 8082
_http_outgoing.js:309
throw new TypeError('The header content contains invalid characters');
^

TypeError: The header content contains invalid characters
at storeHeader (_http_outgoing.js:309:13)
at ServerResponse.OutgoingMessage._storeHeader (_http_outgoing.js:222:9)
at ServerResponse.writeHead (_http_server.js:214:8)
at Server. (/home/dextor/Desktop/tailor-master/example/fragment.js:34:22)
at emitTwo (events.js:87:13)
at Server.emit (events.js:172:7)
at HTTPParser.parserOnIncoming as onIncoming
at HTTPParser.parserOnHeadersComplete (_http_common.js:103:23)

npm ERR! Linux 3.13.0-37-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "run" "example"
npm ERR! node v4.4.4
npm ERR! npm v2.15.1
npm ERR! code ELIFECYCLE
npm ERR! [email protected] example: npm run prepublish && node --harmony example/tailor
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] example script 'npm run prepublish && node --harmony example/tailor'.
npm ERR! This is most likely a problem with the node-tailor package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! npm run prepublish && node --harmony example/tailor
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs node-tailor
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!
npm ERR! npm owner ls node-tailor
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR! /home/dextor/Desktop/tailor-master/npm-debug.log

I have installed all dependencies locally.

Express fragment dependencies?

We have a shell fragment (header + sidebar) and one or many content fragments (mostly a single SPA). Our plan for the shell is to provide certain services to the content fragments via a client-side JavaScript API (e.g. to interact with the sidebar menu or the header search). For that to work, we somehow need to express fragment dependencies or ensure otherwise that the shell bundle is evaluated before any content fragment bundle.

Currently, we implemented this through RequireJS by letting the shell fragment define an AMD module inside of the fragment template, which depends on the shell bundle that is resolved via tailor. I outlined the solution in a gist [1].

Is there any better way to achieve this?

[1] https://gist.github.com/jmaicher/f696b510008adadd94d2b7fbb35c8966

Unable to run tailor examples

When I run npm install after cloning the tailor project I get the following errror. Subsequently if I try to run the basic example using 'node examples/basic', I get 'template not found' error at http://localhost:8080

> [email protected] install /Users/naragon/projects/tailor/node_modules/execSync
> node install.js

[execsync v1.0.2] Attempting to compile native extensions.
[execSync v1.0.2]
    Native code compile failed!!

> [email protected] install /Users/naragon/projects/tailor/node_modules/websocket
> (node-gyp rebuild 2> builderror.log) || (exit 0)

  CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
  SOLINK_MODULE(target) Release/bufferutil.node
  CXX(target) Release/obj.target/validation/src/validation.o
  SOLINK_MODULE(target) Release/validation.node

> [email protected] install /Users/naragon/projects/tailor/node_modules/pre-commit
> node install.js

pre-commit:
pre-commit: Detected an existing git pre-commit hook
pre-commit: Old pre-commit hook backuped to pre-commit.old
pre-commit:

> [email protected] install /Users/naragon/projects/tailor/node_modules/wd
> node scripts/build-browser-scripts

OS: Mac OS X Yosemite (10.10.5)
node: v6.10.0
npm: 3.10.10

Update Tailor to use ES6

Even though NodeJS (along with --harmony flag in v4) provides a lot of ES6 features, the Tailor code doesn't use all of them. Update the code to refactor the Tailor code using ES6. E.g.

  1. Add destructuring to files
fragmentObj = Object.assign({}, { name: node.name, attributes: node.attribs});

to

const { name, attribs: attributes } = node;
fragmentObj = Object.assign({}, { name, attributes});
  1. Use () => to avoid some boilerplate
const stripUrl = (fileUrl) => {
    return path.normalize(fileUrl.replace('file://', ''));
};
const stripUrl = fileUrl => path.normalize(fileUrl.replace('file://', ''));

Custom Require variable

I'm currently using tailor with webpack which means I'm not using require as it's supposed to work. Basically, I'm just using it to load the file if it isn't already loaded. My problem is that the "require" variable is causing conflicts. Would it be possible to have an option to change the "require" variable here:

return new Buffer(memoizedDefinition + `${pipeInstanceName} = new Pipe(require)</script>\n`);

I've replaced the line with: return new Buffer(memoizedDefinition + ${pipeInstanceName} = new Pipe(${options.amdLoaderName || 'require'})</script>\n);

My amd loader file for reference:

(function (window, undefined) {
	let loaded = {};
	function loadModule(src, el) {
        // need to add new script to the browser
        el = window.document.createElement('script');
        el.src = src;
        window.document.head.appendChild(el);
    }
    
	function req(dependencyNames) {
		if (!loaded[dependencyNames[0]]) {
			loaded[dependencyNames[0]] = true;
			loadModule(dependencyNames[0]);
		}
    }
	window['tailor_require'] = req;
}(this));

Node Engine version incorrect

I found that if you run tailor with the node engine version defined in the package (>4.4.X) you get the following error: SyntaxError: Unexpected token ... on this line: fragment.on(eventName, (...args) => {.

My colleague is running node 6.9.0 and that works fine, I am running 4.4.3 which doesn't work.

Allow a way to measure when the main content becomes interactive

Approach planned

  • mark fragments in the template as main - this can be a single / multiple fragments which decides the interactivity of main/meaningful content.
  • primary fragments are by default considered as part of main
  • once all the JS assets for fragment marked as main are initialised, we can calculate the interactivity as
performance.mark('done'); //when all main fragment scripts are executed

performance.measure('interactivity', 'navigationstart', 'done'); //

README clarity sought

A fan of the project says it's not very clear from the README what problems it solves--ie the awesomeness of Tailor isn't fully conveyed. I can edit/add if someone sends bullet points to address his issue. Info that expresses "what this solves and what you can do with it," "this is easy to use because ...," and "this is different from what's already out there because ..." will be great.

Default behaviour of fetchTemplate is too harsh

When I set up a tailor exactly as outlined in the example, and I request a template, that does not exist, an error is thrown, that crashes the server. This behaviour is too harsh and useless in practice. Returning a 404 would be much more useful as a default behaviour.

Even if a a template exists, the tailor as outlined in the readme will crash anyways when accessed by a browser, since the favicon request will result in the file not found error that crashes the server.

Add Functional Programming support to Tailor

The Tailor code can be made more FP compatible using lodash/fp.

Pros: Better code conciseness and readability
Cons: Additon of Lodash dependency

E.g. The below code can be changed using Functional composition as:

module.exports = function filterHeaders (attributes, headers) {
    const newHeaders = {};
    if (attributes.public) {
        return newHeaders;
    };
    ['accept-language', 'referer', 'user-agent'].forEach((key) => {
        if (headers[key]) {
            newHeaders[key] = headers[key];
        }
    });
    return newHeaders;
};

to

const { compose, pick, omitBy } = require('lodash/fp');

module.exports = ({public: publicFragment}, headers) => 
    publicFragment ? {} : compose(
        omitBy(e => !e),
        pick(['accept-language', 'referer', 'user-agent'])
    )(headers)

Example doesn't work

Exception:

~/code/tailor/tailor-src$ npm run example

[email protected] example /Users/akeelnazir/code/tailor/tailor-src
npm run prepublish && node --harmony example/tailor

[email protected] prepublish /Users/akeelnazir/code/tailor/tailor-src
uglifyjs src/pipe.js --mangle --compress --comments -o src/pipe.min.js

Tailor started at port 8080
Fragment1 started at port 8081
Fragment2 started at port 8082
Fragment3 started at port 8083
fs.js:634
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
^

Error: ENOENT: no such file or directory, open '/Users/akeelnazir/code/tailor/tailor-src/example/templates/.html'
at Error (native)
at Object.fs.openSync (fs.js:634:18)
at Object.fs.readFileSync (fs.js:502:33)
at /Users/akeelnazir/code/tailor/tailor-src/lib/fetch-template.js:11:31
at Tailor.processRequest (/Users/akeelnazir/code/tailor/tailor-src/lib/request-handler.js:24:29)
at emitTwo (events.js:106:13)
at Server.emit (events.js:191:7)
at HTTPParser.parserOnIncoming as onIncoming
at HTTPParser.parserOnHeadersComplete (_http_common.js:105:23)

filename: npm-debug.log

0 info it worked if it ends with ok
1 verbose cli [ '/Users/akeelnazir/n/bin/node',
1 verbose cli '/Users/akeelnazir/n/bin/npm',
1 verbose cli 'run',
1 verbose cli 'example' ]
2 info using [email protected]
3 info using [email protected]
4 verbose run-script [ 'preexample', 'example', 'postexample' ]
5 info lifecycle [email protected]preexample: [email protected]
6 silly lifecycle [email protected]
preexample: no script for preexample, continuing
7 info lifecycle [email protected]example: [email protected]
8 verbose lifecycle [email protected]
example: unsafe-perm in lifecycle true
9 verbose lifecycle [email protected]example: PATH: /Users/akeelnazir/n/lib/node_modules/npm/bin/node-gyp-bin:/Users/akeelnazir/code/tailor/tailor-src/node_modules/.bin:/Users/akeelnazir/n/bin:/Users/akeelnazir/Downloads/google-cloud-sdk/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/akeelnazir/n/bin:/Users/akeelnazir/kubernetes/cluster
10 verbose lifecycle [email protected]
example: CWD: /Users/akeelnazir/code/tailor/tailor-src
11 silly lifecycle [email protected]example: Args: [ '-c', 'npm run prepublish && node --harmony example/tailor' ]
12 silly lifecycle [email protected]
example: Returned: code: 1 signal: null
13 info lifecycle [email protected]~example: Failed to exec example script
14 verbose stack Error: [email protected] example: npm run prepublish && node --harmony example/tailor
14 verbose stack Exit status 1
14 verbose stack at EventEmitter. (/Users/akeelnazir/n/lib/node_modules/npm/lib/utils/lifecycle.js:245:16)
14 verbose stack at emitTwo (events.js:106:13)
14 verbose stack at EventEmitter.emit (events.js:191:7)
14 verbose stack at ChildProcess. (/Users/akeelnazir/n/lib/node_modules/npm/lib/utils/spawn.js:24:14)
14 verbose stack at emitTwo (events.js:106:13)
14 verbose stack at ChildProcess.emit (events.js:191:7)
14 verbose stack at maybeClose (internal/child_process.js:852:16)
14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5)
15 verbose pkgid [email protected]
16 verbose cwd /Users/akeelnazir/code/tailor/tailor-src
17 error Darwin 15.6.0
18 error argv "/Users/akeelnazir/n/bin/node" "/Users/akeelnazir/n/bin/npm" "run" "example"
19 error node v6.2.1
20 error npm v3.9.3
21 error code ELIFECYCLE
22 error [email protected] example: npm run prepublish && node --harmony example/tailor
22 error Exit status 1
23 error Failed at the [email protected] example script 'npm run prepublish && node --harmony example/tailor'.
23 error Make sure you have the latest version of node.js and npm installed.
23 error If you do, this is most likely a problem with the node-tailor package,
23 error not with npm itself.
23 error Tell the author that this fails on your system:
23 error npm run prepublish && node --harmony example/tailor
23 error You can get information on how to open an issue for this project with:
23 error npm bugs node-tailor
23 error Or if that isn't available, you can get their info via:
23 error npm owner ls node-tailor
23 error There is likely additional logging output above.
24 verbose exit [ 1, true ]

Asset fragment in head is being ignored

I'm working on this SPA example where I'm trying to follow the pattern in your readme that shows an assets fragment in the head section.

`

`

But I'm showing that tailor is not trying to pull in my assets fragment.

Tailor started at port 8080 Fragment Header started at port 8081 Fragment Assets started at port 8082 / /header.css /header.css /header.js

I end up with this markup in the head section, which is there because of the fragment in the body to bring in the header.

<script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="http://localhost:8081/header.js" src="http://localhost:8081/header.js"></script>

Link tech blog articles and conference videos related to Skipper in the readme

Idea

  1. In order to give people a bit more background why Tailor exists and what alternatives where considered it would be great to link this blog article in the README.md:

  2. Are there also some conference talks related to Tailor available we could show?

Motivation

I got the feedback from people that are checking the projects on Zalando github overview page that it's hard to get the context of projects. This could help these people to understand the purpose of this project better.

What do you think, @vigneshshanmugam ?

handle 'close' event in http.Server streams

Hi there,
there are 'close' events in node.js http.IncomeMessage and http.ServerResponse streams, and these events mean that connection was closed before response was fully passed to client (before 'end' event). It means that if you don’t handle this event on server side, your server will pipe all data through stream which has no consumer.

It's not a bug itself, it's just a possible place for avoiding extra needless work.

I wrote a simple client.js script and added logs to tailor server from your example.
tadjik1@30db7e1
Just run these commands in different terminals

npm run example
node example/client.js

and check that there are different values in received and sent logs.

received 3219
sent 4678

Roadmap/Enhancements

  • Prioritise fragments based on fast/slow response (Out of order delivery? )
  • Handle Single Page Application
  • Avoid FOUT for async fragments
  • Service Workers

How to forward query and path parameters?

Hi,

I'm trying to find a way to forward the query string (e.g. 'query=string' in http://host.com/some/123?query=string") to my fragment servers.

I also try to find a solution for path parameters (e.g. '123' in above example) . I know that it's possible to replace the default implementation of fetch-template to not match the whole path name and that I can transform path parameters to query strings with skipper. But is there an easier / less intrusive way to achieve this?

Thanks a lot
Jan

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.