npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hula-hoop

v1.2.0

Published

Thorax server-side Hapi components

Downloads

56

Readme

Hula-Hoop

Server-side rendering components for Circus/Lumbar + Thorax + Hapi stacks.

Provides common endpoints and libraries implementing:

  • Route-based Fruit-loops server-side rendering
  • Automatic fail over to client-side rendering
  • Application resource loading and serving
  • Conditional branch loading

Usage

var HulaHoop = require('hula-hoop');

// Setup resource handling
HulaHoop.api.resourceLoader.register(appName, [
  {name: 'main', version: '1.0.0', path: './build'}
]);
server.route([
  {
    path: '/r/{path*}',
    method: 'GET',
    handler: HulaHoop.endpoints.resources(),
    config: {
      cache: {
        expiresIn: 365*24*60*60*1000
      }
    }
  }
]);

// Setup the user endpoint routing
var pageHandler = HulaHoop.endpoints.page(appName, {
  host: 'foo.com',
  resourceRoot: '/r/'
});

server.route(
  HulaHoop.api.resourceLoader.routes().map(function(route) {
    return {
      path: route,
      method: 'GET',
      handler: pageHandler,
      config: {
        cache: {
          expiresIn: 5*60*1000,
          privacy: 'private'
        }
      }
    };
  })
);

This assumes that ./build has the contents of the built Thorax application and the either the module-map.json file has been generated via the hapi-routes grunt task or the circus.json file has been properly constructed via the route converter plugin.

Server vs. Client Side Rendering

Hula-hoop is able to conditionally return either server rendered HTML content or a simplified page suitable for complete client-side rendering. This optimizes the use of Fruit Loops page instances to the cases that are going to benefit and allows for rendering failover, should errors occur in supported server side cases.

This behavior is controlled via the project config file and generally should be transparent for most users. It may be AB tested via the serverRoute config option, discussed in the endpoints.page documentation below.

Resources and Branches

Hula-hoop maintains a list of all resources that are involved with running a given Thorax application via the api.resourceLoader API. Resources registered through this API can easily be served to clients and concurrently used for server-side rendering.

Additionally, it can serve multiple versions of an application on the same endpoint in support of application-level AB testing or staged deploys. This is done by swapping the served index files through the resourceLoader.index API.

API

endpoints.page(app, options)

Conditionally renders the requests using client-side or server-side rendering based on:

  • serverRoute flag specified in project config file
  • userConfig flag serverRoute flag
    • serverRoute === false disabled all server-side rendering
    • serverRoute[hapiPath] === false disable server-side rendering for a specific route

Should an error occur while rendering the server-side page, the response will failover to the client-side rendering after logging the response.

options is the combined list of options for both the endpoints.serverSide and endpoints.clientSide handlers discussed below.

One distinction is that finalize is passed an additional parameter specifying whether or not server side rendering was used to generate the final response. finalize(req, response, wasServerSide)

On caching, this endpoint will emit custom cache headers if server-side rendering is utilized, based on the resultant cache calculated from the AJAX requests made during the page's execution cycle. Any cache values defined on the hapi route definition will be utilized for the client-side response only.

endpoints.serverSide(options)

Renders the given request using the server-side rendering engine. Errors are treated as fatal WRT the response lifecycle.

Options include:

  • host: Host value used to generate window.location and perform relative AJAX calls. Example: github.com.
  • resourcesRoot: Root prefix that is stripped to map client resources to server files. Example: /resources/scripts
  • poolSize: Maximum number of page instances to maintain in the pool. This defaults to 5 and should be tuned for both the memory and traffic load of particular applications while in production.
  • cacheResources: Boolean flag. True to cache loaded html and script resources indefinitely.
  • publicConfig(req, reply): Used to generate the configuration for a given request. Used primarily to retrieve the branch field to select the rendering branch to utilize.
  • ajaxCache: Catbox cache used to cache AJAX responses retrieved via page execution.
  • beforeExec(page, next): Fruit Loops pool beforeExec method. Can be used to initialize pages to known states. Example:
    function(page, next) {
      // Provide a default seed value (and also avoid a NPE in lumbar loader due to missing
      // screen object
      page.window.devicePixelRatio = 2;
      page.window.phoenixConfig = siteConfig;
      next();
    }
  • beforeNavigate(page, isExistingPage, next): Optional callback called before Backbone's route handlers. Must call next once complete to continue page execution.
  • cleanup(page, next): Fruit loops Pool cleanup method. Used to perform cleanup after a response has been sent to the user, but before the page object is returned to the pool. Example:
    function(page, next) {
      page.window.Backbone.history.trigger('cleanup');

      next();
    }
  • maxServerExpires: Maximum cache timeout for server side responses, in seconds. Defaults to 24 hours.

Many of theses options map directly to Fruit Loops Pool options, whose documentation contains additional information.

Logging:

  • ['server-side', 'pool']: Navigation started. Includes page instances in existence and free as well as the number of queued requests in the data section.
  • ['server-side', 'error']: Error occurred while executing application logic.
  • ['server-side', 'exec']: Successful server-side rendering execution. Data includes various metadata regarding the response.

endpoints.clientSide(app, options)

Renders the index for a given app in a manner suitable for client side rendering. If a config is generated for the user, this will be inlined in the response.

options:

  • configVar: Required JavaScript identifier that will receive the inlined config. This may be any valid, assignable JavaScript construct. Ex: var foo, Foo.bar.
  • userConfig(req): Used to generate the configuration for a given request. The return value's branch field will be used to select the resource branch to render.
  • finalize(req, response): Allows arbitrary modification of response prior to successful return. This can be used to attach state or custom headers to the response, etc.
  • heartbeat: Truthy to enable simplified success only response

This will serve the index file that is associated with the app. Dependent resources for a particular route can be defined in the js and css fields on the route info object (See api.resourceLoader.routeInfo) and will be inserted wherever <!-- hula-hoop: scripts --> and <!-- hula-hoop: links --> comments are found in the index file.

These comments can also implement a fail-over through begin and end pairs:

<!-- hula-hoop: begin scripts -->
<script src="failover"></script>
<!-- hula-hoop: end scripts -->

Which will be removed when run within hula-hoop, allowing for other environments such as test environments to execute on the same index file.

api.resourceLoader

Maintains a versioned list of files and routes associated with the webapp.

.register(app, resources)

Registers the set of resources supported by a given application.

  • app: any unique string that identifies this particular application
  • resources: Array of resource objects:
  [
    {
      "name": "a",
      "version": "1.0.0",
      "path": "./mweb-a"
    },
    {
      "name": "b",
      "version": "1.1.0",
      "path": "./mweb-b"
    }
  ]

Each resource object defines:

  • name: Name of the branch for this resource. This is the value used when using the index API below.
  • version: Semver value of this version. If omitted name will be used to determine the default index file.
  • path: Path to the directory containing this branch's source.

Within the resource directory, the index.html, circus.json, and module-map.json files are given special treatment. All other files must not have duplicates and should be versioned via a system such as lumbar-long-expires or webpack hashes.

The project configuration file, module-map.json or circus.json, contains the route information for the application and will not be exposed as a resource available for consumption on the asset or assetContainer APIs. If using Lumbar, this can be generated using the hapi-routes grunt task included in this project and webpack builds using the router converter plugin.

.index(app, branch)

Retrieves the index file path for a given application and branch. Should the branch not be found, the index file from the highest versioned branch will be returned. Branches that are not versioned will use the last sorted name.

.asset(path)

Retrieves the file system path for a given resource path.

.assetContainer(path)

Retrieves the root directory that contains the file with the given resource path.

.routeInfo(branch, route)

Returns the module map metadata for a given app branch and route.

.routes()

Returns all routes supported by all registered apps and versions.

Grunt Tasks

hapi-routes

Generates the module-map file utilized by resourceLoader to track the routes supported by a given application.

  grunt.initConfig({
    'hapi-routes': {
      map: {
        options: {
          package: 'web',
          dest: buildDir + '/module-map.json'
        }
      },
      release: {
        options: {
          package: 'web',
          dest: 'build/' + releasePrefix + '/module-map.json'
        }
      }
    }
  });

  grunt.loadNpmTasks('hula-hoop');

Note that using this task requires that lumbar be installed as a sibling of hula-hoops at build time. Any arguments passed in the options field will be forwarded to the lumbar constructor, allowing for plugins and libraries to be specified.

Webpack Plugins

route-converter

Generates a hulahoop structure for circus.json, used to configure hula-hoop at runtime.

This should be passed in to the webpack plugin configuration object.

var RouteConverterPlugin = require('hula-hoop/webpack/router-converter');

...

  plugins: [
    new RouteConverterPlugin()
  ]

...