diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..a5d6b88
Binary files /dev/null and b/.DS_Store differ
diff --git a/README.md b/README.md
index 503a8f9..76aff2d 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,9 @@
-# passion-project
\ No newline at end of file
+# Grub Monitor
+
+###Description
+Grub Monitor is an iOS app that helps you keep track of your groceries. This will prevent the waste of expired food and give you recipes to utilize soon to be expired food.
+
+###Basic Functionalities
+* add groceries to a list
+* get notifications of expired food
+* get recipes with ingredients
diff --git a/grocery-api/.gitignore b/grocery-api/.gitignore
new file mode 100644
index 0000000..050c9d9
--- /dev/null
+++ b/grocery-api/.gitignore
@@ -0,0 +1,17 @@
+# See https://help.github.com/articles/ignoring-files for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile '~/.gitignore_global'
+
+# Ignore bundler config.
+/.bundle
+
+# Ignore the default SQLite database.
+/db/*.sqlite3
+/db/*.sqlite3-journal
+
+# Ignore all logfiles and tempfiles.
+/log/*
+!/log/.keep
+/tmp
diff --git a/grocery-api/Gemfile b/grocery-api/Gemfile
new file mode 100644
index 0000000..85624d9
--- /dev/null
+++ b/grocery-api/Gemfile
@@ -0,0 +1,47 @@
+source 'https://rubygems.org'
+
+
+# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
+gem 'rails', '4.2.5.1'
+# Use sqlite3 as the database for Active Record
+gem 'sqlite3'
+# Use SCSS for stylesheets
+gem 'sass-rails', '~> 5.0'
+# Use Uglifier as compressor for JavaScript assets
+gem 'uglifier', '>= 1.3.0'
+# Use CoffeeScript for .coffee assets and views
+gem 'coffee-rails', '~> 4.1.0'
+# See https://github.com/rails/execjs#readme for more supported runtimes
+# gem 'therubyracer', platforms: :ruby
+
+# Use jquery as the JavaScript library
+gem 'jquery-rails'
+# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
+gem 'turbolinks'
+# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
+gem 'jbuilder', '~> 2.0'
+# bundle exec rake doc:rails generates the API under doc/api.
+gem 'sdoc', '~> 0.4.0', group: :doc
+
+# Use ActiveModel has_secure_password
+# gem 'bcrypt', '~> 3.1.7'
+
+# Use Unicorn as the app server
+# gem 'unicorn'
+
+# Use Capistrano for deployment
+# gem 'capistrano-rails', group: :development
+
+group :development, :test do
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
+ gem 'byebug'
+end
+
+group :development do
+ # Access an IRB console on exception pages or by using <%= console %> in views
+ gem 'web-console', '~> 2.0'
+
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
+ gem 'spring'
+end
+
diff --git a/grocery-api/Gemfile.lock b/grocery-api/Gemfile.lock
new file mode 100644
index 0000000..5ab2ca9
--- /dev/null
+++ b/grocery-api/Gemfile.lock
@@ -0,0 +1,159 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actionmailer (4.2.5.1)
+ actionpack (= 4.2.5.1)
+ actionview (= 4.2.5.1)
+ activejob (= 4.2.5.1)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ actionpack (4.2.5.1)
+ actionview (= 4.2.5.1)
+ activesupport (= 4.2.5.1)
+ rack (~> 1.6)
+ rack-test (~> 0.6.2)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ actionview (4.2.5.1)
+ activesupport (= 4.2.5.1)
+ builder (~> 3.1)
+ erubis (~> 2.7.0)
+ rails-dom-testing (~> 1.0, >= 1.0.5)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ activejob (4.2.5.1)
+ activesupport (= 4.2.5.1)
+ globalid (>= 0.3.0)
+ activemodel (4.2.5.1)
+ activesupport (= 4.2.5.1)
+ builder (~> 3.1)
+ activerecord (4.2.5.1)
+ activemodel (= 4.2.5.1)
+ activesupport (= 4.2.5.1)
+ arel (~> 6.0)
+ activesupport (4.2.5.1)
+ i18n (~> 0.7)
+ json (~> 1.7, >= 1.7.7)
+ minitest (~> 5.1)
+ thread_safe (~> 0.3, >= 0.3.4)
+ tzinfo (~> 1.1)
+ arel (6.0.3)
+ binding_of_caller (0.7.2)
+ debug_inspector (>= 0.0.1)
+ builder (3.2.2)
+ byebug (8.2.2)
+ coffee-rails (4.1.1)
+ coffee-script (>= 2.2.0)
+ railties (>= 4.0.0, < 5.1.x)
+ coffee-script (2.4.1)
+ coffee-script-source
+ execjs
+ coffee-script-source (1.10.0)
+ concurrent-ruby (1.0.0)
+ debug_inspector (0.0.2)
+ erubis (2.7.0)
+ execjs (2.6.0)
+ globalid (0.3.6)
+ activesupport (>= 4.1.0)
+ i18n (0.7.0)
+ jbuilder (2.4.1)
+ activesupport (>= 3.0.0, < 5.1)
+ multi_json (~> 1.2)
+ jquery-rails (4.1.0)
+ rails-dom-testing (~> 1.0)
+ railties (>= 4.2.0)
+ thor (>= 0.14, < 2.0)
+ json (1.8.3)
+ loofah (2.0.3)
+ nokogiri (>= 1.5.9)
+ mail (2.6.3)
+ mime-types (>= 1.16, < 3)
+ mime-types (2.99)
+ mini_portile2 (2.0.0)
+ minitest (5.8.4)
+ multi_json (1.11.2)
+ nokogiri (1.6.7.2)
+ mini_portile2 (~> 2.0.0.rc2)
+ rack (1.6.4)
+ rack-test (0.6.3)
+ rack (>= 1.0)
+ rails (4.2.5.1)
+ actionmailer (= 4.2.5.1)
+ actionpack (= 4.2.5.1)
+ actionview (= 4.2.5.1)
+ activejob (= 4.2.5.1)
+ activemodel (= 4.2.5.1)
+ activerecord (= 4.2.5.1)
+ activesupport (= 4.2.5.1)
+ bundler (>= 1.3.0, < 2.0)
+ railties (= 4.2.5.1)
+ sprockets-rails
+ rails-deprecated_sanitizer (1.0.3)
+ activesupport (>= 4.2.0.alpha)
+ rails-dom-testing (1.0.7)
+ activesupport (>= 4.2.0.beta, < 5.0)
+ nokogiri (~> 1.6.0)
+ rails-deprecated_sanitizer (>= 1.0.1)
+ rails-html-sanitizer (1.0.3)
+ loofah (~> 2.0)
+ railties (4.2.5.1)
+ actionpack (= 4.2.5.1)
+ activesupport (= 4.2.5.1)
+ rake (>= 0.8.7)
+ thor (>= 0.18.1, < 2.0)
+ rake (10.5.0)
+ rdoc (4.2.2)
+ json (~> 1.4)
+ sass (3.4.21)
+ sass-rails (5.0.4)
+ railties (>= 4.0.0, < 5.0)
+ sass (~> 3.1)
+ sprockets (>= 2.8, < 4.0)
+ sprockets-rails (>= 2.0, < 4.0)
+ tilt (>= 1.1, < 3)
+ sdoc (0.4.1)
+ json (~> 1.7, >= 1.7.7)
+ rdoc (~> 4.0)
+ spring (1.6.3)
+ sprockets (3.5.2)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.0.1)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0)
+ sqlite3 (1.3.11)
+ thor (0.19.1)
+ thread_safe (0.3.5)
+ tilt (2.0.2)
+ turbolinks (2.5.3)
+ coffee-rails
+ tzinfo (1.2.2)
+ thread_safe (~> 0.1)
+ uglifier (2.7.2)
+ execjs (>= 0.3.0)
+ json (>= 1.8.0)
+ web-console (2.3.0)
+ activemodel (>= 4.0)
+ binding_of_caller (>= 0.7.2)
+ railties (>= 4.0)
+ sprockets-rails (>= 2.0, < 4.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ byebug
+ coffee-rails (~> 4.1.0)
+ jbuilder (~> 2.0)
+ jquery-rails
+ rails (= 4.2.5.1)
+ sass-rails (~> 5.0)
+ sdoc (~> 0.4.0)
+ spring
+ sqlite3
+ turbolinks
+ uglifier (>= 1.3.0)
+ web-console (~> 2.0)
+
+BUNDLED WITH
+ 1.10.6
diff --git a/grocery-api/README.rdoc b/grocery-api/README.rdoc
new file mode 100644
index 0000000..dd4e97e
--- /dev/null
+++ b/grocery-api/README.rdoc
@@ -0,0 +1,28 @@
+== README
+
+This README would normally document whatever steps are necessary to get the
+application up and running.
+
+Things you may want to cover:
+
+* Ruby version
+
+* System dependencies
+
+* Configuration
+
+* Database creation
+
+* Database initialization
+
+* How to run the test suite
+
+* Services (job queues, cache servers, search engines, etc.)
+
+* Deployment instructions
+
+* ...
+
+
+Please feel free to use a different markup language if you do not plan to run
+rake doc:app.
diff --git a/grocery-api/Rakefile b/grocery-api/Rakefile
new file mode 100644
index 0000000..ba6b733
--- /dev/null
+++ b/grocery-api/Rakefile
@@ -0,0 +1,6 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+
+Rails.application.load_tasks
diff --git a/grocery-api/app/assets/images/.keep b/grocery-api/app/assets/images/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/app/assets/javascripts/application.js b/grocery-api/app/assets/javascripts/application.js
new file mode 100644
index 0000000..e07c5a8
--- /dev/null
+++ b/grocery-api/app/assets/javascripts/application.js
@@ -0,0 +1,16 @@
+// This is a manifest file that'll be compiled into application.js, which will include all the files
+// listed below.
+//
+// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
+// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
+//
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// compiled file.
+//
+// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
+// about supported directives.
+//
+//= require jquery
+//= require jquery_ujs
+//= require turbolinks
+//= require_tree .
diff --git a/grocery-api/app/assets/javascripts/foods.coffee b/grocery-api/app/assets/javascripts/foods.coffee
new file mode 100644
index 0000000..24f83d1
--- /dev/null
+++ b/grocery-api/app/assets/javascripts/foods.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/grocery-api/app/assets/stylesheets/application.css b/grocery-api/app/assets/stylesheets/application.css
new file mode 100644
index 0000000..f9cd5b3
--- /dev/null
+++ b/grocery-api/app/assets/stylesheets/application.css
@@ -0,0 +1,15 @@
+/*
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
+ * listed below.
+ *
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
+ *
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
+ * compiled file so the styles you add here take precedence over styles defined in any styles
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
+ * file per style scope.
+ *
+ *= require_tree .
+ *= require_self
+ */
diff --git a/grocery-api/app/assets/stylesheets/foods.scss b/grocery-api/app/assets/stylesheets/foods.scss
new file mode 100644
index 0000000..d792bb1
--- /dev/null
+++ b/grocery-api/app/assets/stylesheets/foods.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the foods controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/grocery-api/app/controllers/application_controller.rb b/grocery-api/app/controllers/application_controller.rb
new file mode 100644
index 0000000..d83690e
--- /dev/null
+++ b/grocery-api/app/controllers/application_controller.rb
@@ -0,0 +1,5 @@
+class ApplicationController < ActionController::Base
+ # Prevent CSRF attacks by raising an exception.
+ # For APIs, you may want to use :null_session instead.
+ protect_from_forgery with: :exception
+end
diff --git a/grocery-api/app/controllers/concerns/.keep b/grocery-api/app/controllers/concerns/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/app/controllers/foods_controller.rb b/grocery-api/app/controllers/foods_controller.rb
new file mode 100644
index 0000000..97b5eaa
--- /dev/null
+++ b/grocery-api/app/controllers/foods_controller.rb
@@ -0,0 +1,5 @@
+class FoodsController < ApplicationController
+ def index
+ @foods = Food.all
+ end
+end
diff --git a/grocery-api/app/helpers/application_helper.rb b/grocery-api/app/helpers/application_helper.rb
new file mode 100644
index 0000000..de6be79
--- /dev/null
+++ b/grocery-api/app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
diff --git a/grocery-api/app/helpers/foods_helper.rb b/grocery-api/app/helpers/foods_helper.rb
new file mode 100644
index 0000000..4bad757
--- /dev/null
+++ b/grocery-api/app/helpers/foods_helper.rb
@@ -0,0 +1,2 @@
+module FoodsHelper
+end
diff --git a/grocery-api/app/mailers/.keep b/grocery-api/app/mailers/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/app/models/.keep b/grocery-api/app/models/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/app/models/concerns/.keep b/grocery-api/app/models/concerns/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/app/models/food.rb b/grocery-api/app/models/food.rb
new file mode 100644
index 0000000..3f58467
--- /dev/null
+++ b/grocery-api/app/models/food.rb
@@ -0,0 +1,2 @@
+class Food < ActiveRecord::Base
+end
diff --git a/grocery-api/app/views/foods/index.html.erb b/grocery-api/app/views/foods/index.html.erb
new file mode 100644
index 0000000..f84ff7e
--- /dev/null
+++ b/grocery-api/app/views/foods/index.html.erb
@@ -0,0 +1,2 @@
+
Foods#index
+Find me in app/views/foods/index.html.erb
diff --git a/grocery-api/app/views/foods/index.json.jbuilder b/grocery-api/app/views/foods/index.json.jbuilder
new file mode 100644
index 0000000..a5d0d32
--- /dev/null
+++ b/grocery-api/app/views/foods/index.json.jbuilder
@@ -0,0 +1,4 @@
+json.foods @foods do |food|
+ json.name food.name
+ json.expiration_date food.duration_seconds.seconds.from_now
+end
\ No newline at end of file
diff --git a/grocery-api/app/views/layouts/application.html.erb b/grocery-api/app/views/layouts/application.html.erb
new file mode 100644
index 0000000..501b0be
--- /dev/null
+++ b/grocery-api/app/views/layouts/application.html.erb
@@ -0,0 +1,14 @@
+
+
+
+ GroceryApi
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
+ <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
+ <%= csrf_meta_tags %>
+
+
+
+<%= yield %>
+
+
+
diff --git a/grocery-api/bin/bundle b/grocery-api/bin/bundle
new file mode 100755
index 0000000..66e9889
--- /dev/null
+++ b/grocery-api/bin/bundle
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+load Gem.bin_path('bundler', 'bundle')
diff --git a/grocery-api/bin/rails b/grocery-api/bin/rails
new file mode 100755
index 0000000..0138d79
--- /dev/null
+++ b/grocery-api/bin/rails
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+begin
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
+end
+APP_PATH = File.expand_path('../../config/application', __FILE__)
+require_relative '../config/boot'
+require 'rails/commands'
diff --git a/grocery-api/bin/rake b/grocery-api/bin/rake
new file mode 100755
index 0000000..d87d5f5
--- /dev/null
+++ b/grocery-api/bin/rake
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+begin
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
+end
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
diff --git a/grocery-api/bin/setup b/grocery-api/bin/setup
new file mode 100755
index 0000000..acdb2c1
--- /dev/null
+++ b/grocery-api/bin/setup
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+require 'pathname'
+
+# path to your application root.
+APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
+
+Dir.chdir APP_ROOT do
+ # This script is a starting point to setup your application.
+ # Add necessary setup steps to this file:
+
+ puts "== Installing dependencies =="
+ system "gem install bundler --conservative"
+ system "bundle check || bundle install"
+
+ # puts "\n== Copying sample files =="
+ # unless File.exist?("config/database.yml")
+ # system "cp config/database.yml.sample config/database.yml"
+ # end
+
+ puts "\n== Preparing database =="
+ system "bin/rake db:setup"
+
+ puts "\n== Removing old logs and tempfiles =="
+ system "rm -f log/*"
+ system "rm -rf tmp/cache"
+
+ puts "\n== Restarting application server =="
+ system "touch tmp/restart.txt"
+end
diff --git a/grocery-api/bin/spring b/grocery-api/bin/spring
new file mode 100755
index 0000000..62ec28f
--- /dev/null
+++ b/grocery-api/bin/spring
@@ -0,0 +1,15 @@
+#!/usr/bin/env ruby
+
+# This file loads spring without using Bundler, in order to be fast.
+# It gets overwritten when you run the `spring binstub` command.
+
+unless defined?(Spring)
+ require 'rubygems'
+ require 'bundler'
+
+ if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m))
+ Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq }
+ gem 'spring', match[1]
+ require 'spring/binstub'
+ end
+end
diff --git a/grocery-api/config.ru b/grocery-api/config.ru
new file mode 100644
index 0000000..bd83b25
--- /dev/null
+++ b/grocery-api/config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Rails.application
diff --git a/grocery-api/config/application.rb b/grocery-api/config/application.rb
new file mode 100644
index 0000000..cb70da7
--- /dev/null
+++ b/grocery-api/config/application.rb
@@ -0,0 +1,26 @@
+require File.expand_path('../boot', __FILE__)
+
+require 'rails/all'
+
+# Require the gems listed in Gemfile, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(*Rails.groups)
+
+module GroceryApi
+ class Application < Rails::Application
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+ # config.i18n.default_locale = :de
+
+ # Do not swallow errors in after_commit/after_rollback callbacks.
+ config.active_record.raise_in_transactional_callbacks = true
+ end
+end
diff --git a/grocery-api/config/boot.rb b/grocery-api/config/boot.rb
new file mode 100644
index 0000000..6b750f0
--- /dev/null
+++ b/grocery-api/config/boot.rb
@@ -0,0 +1,3 @@
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+
+require 'bundler/setup' # Set up gems listed in the Gemfile.
diff --git a/grocery-api/config/database.yml b/grocery-api/config/database.yml
new file mode 100644
index 0000000..1c1a37c
--- /dev/null
+++ b/grocery-api/config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+#
+default: &default
+ adapter: sqlite3
+ pool: 5
+ timeout: 5000
+
+development:
+ <<: *default
+ database: db/development.sqlite3
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: db/test.sqlite3
+
+production:
+ <<: *default
+ database: db/production.sqlite3
diff --git a/grocery-api/config/environment.rb b/grocery-api/config/environment.rb
new file mode 100644
index 0000000..ee8d90d
--- /dev/null
+++ b/grocery-api/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the Rails application.
+require File.expand_path('../application', __FILE__)
+
+# Initialize the Rails application.
+Rails.application.initialize!
diff --git a/grocery-api/config/environments/development.rb b/grocery-api/config/environments/development.rb
new file mode 100644
index 0000000..b55e214
--- /dev/null
+++ b/grocery-api/config/environments/development.rb
@@ -0,0 +1,41 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Don't care if the mailer can't send.
+ config.action_mailer.raise_delivery_errors = false
+
+ # Print deprecation notices to the Rails logger.
+ config.active_support.deprecation = :log
+
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ # Debug mode disables concatenation and preprocessing of assets.
+ # This option may cause significant delays in view rendering with a large
+ # number of complex assets.
+ config.assets.debug = true
+
+ # Asset digests allow you to set far-future HTTP expiration dates on all assets,
+ # yet still be able to expire them through the digest params.
+ config.assets.digest = true
+
+ # Adds additional error checking when serving assets at runtime.
+ # Checks for improperly declared sprockets dependencies.
+ # Raises helpful error messages.
+ config.assets.raise_runtime_errors = true
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/grocery-api/config/environments/production.rb b/grocery-api/config/environments/production.rb
new file mode 100644
index 0000000..5c1b32e
--- /dev/null
+++ b/grocery-api/config/environments/production.rb
@@ -0,0 +1,79 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
+ config.cache_classes = true
+
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both threaded web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Enable Rack::Cache to put a simple HTTP cache in front of your application
+ # Add `rack-cache` to your Gemfile before enabling this.
+ # For large-scale production use, consider using a caching reverse proxy like
+ # NGINX, varnish or squid.
+ # config.action_dispatch.rack_cache = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
+
+ # Compress JavaScripts and CSS.
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
+
+ # Do not fallback to assets pipeline if a precompiled asset is missed.
+ config.assets.compile = false
+
+ # Asset digests allow you to set far-future HTTP expiration dates on all assets,
+ # yet still be able to expire them through the digest params.
+ config.assets.digest = true
+
+ # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # Use the lowest log level to ensure availability of diagnostic information
+ # when problems arise.
+ config.log_level = :debug
+
+ # Prepend all log lines with the following tags.
+ # config.log_tags = [ :subdomain, :uuid ]
+
+ # Use a different logger for distributed setups.
+ # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
+
+ # Use a different cache store in production.
+ # config.cache_store = :mem_cache_store
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation cannot be found).
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners.
+ config.active_support.deprecation = :notify
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
+end
diff --git a/grocery-api/config/environments/test.rb b/grocery-api/config/environments/test.rb
new file mode 100644
index 0000000..1c19f08
--- /dev/null
+++ b/grocery-api/config/environments/test.rb
@@ -0,0 +1,42 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
+ # Configure static file server for tests with Cache-Control for performance.
+ config.serve_static_files = true
+ config.static_cache_control = 'public, max-age=3600'
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates.
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Randomize the order test cases are executed.
+ config.active_support.test_order = :random
+
+ # Print deprecation notices to the stderr.
+ config.active_support.deprecation = :stderr
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/grocery-api/config/initializers/assets.rb b/grocery-api/config/initializers/assets.rb
new file mode 100644
index 0000000..01ef3e6
--- /dev/null
+++ b/grocery-api/config/initializers/assets.rb
@@ -0,0 +1,11 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = '1.0'
+
+# Add additional assets to the asset load path
+# Rails.application.config.assets.paths << Emoji.images_path
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
+# Rails.application.config.assets.precompile += %w( search.js )
diff --git a/grocery-api/config/initializers/backtrace_silencers.rb b/grocery-api/config/initializers/backtrace_silencers.rb
new file mode 100644
index 0000000..59385cd
--- /dev/null
+++ b/grocery-api/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/grocery-api/config/initializers/cookies_serializer.rb b/grocery-api/config/initializers/cookies_serializer.rb
new file mode 100644
index 0000000..7f70458
--- /dev/null
+++ b/grocery-api/config/initializers/cookies_serializer.rb
@@ -0,0 +1,3 @@
+# Be sure to restart your server when you modify this file.
+
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/grocery-api/config/initializers/filter_parameter_logging.rb b/grocery-api/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000..4a994e1
--- /dev/null
+++ b/grocery-api/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure sensitive parameters which will be filtered from the log file.
+Rails.application.config.filter_parameters += [:password]
diff --git a/grocery-api/config/initializers/inflections.rb b/grocery-api/config/initializers/inflections.rb
new file mode 100644
index 0000000..ac033bf
--- /dev/null
+++ b/grocery-api/config/initializers/inflections.rb
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/grocery-api/config/initializers/mime_types.rb b/grocery-api/config/initializers/mime_types.rb
new file mode 100644
index 0000000..dc18996
--- /dev/null
+++ b/grocery-api/config/initializers/mime_types.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
diff --git a/grocery-api/config/initializers/session_store.rb b/grocery-api/config/initializers/session_store.rb
new file mode 100644
index 0000000..d5c0566
--- /dev/null
+++ b/grocery-api/config/initializers/session_store.rb
@@ -0,0 +1,3 @@
+# Be sure to restart your server when you modify this file.
+
+Rails.application.config.session_store :cookie_store, key: '_grocery-api_session'
diff --git a/grocery-api/config/initializers/wrap_parameters.rb b/grocery-api/config/initializers/wrap_parameters.rb
new file mode 100644
index 0000000..33725e9
--- /dev/null
+++ b/grocery-api/config/initializers/wrap_parameters.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
+end
+
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/grocery-api/config/locales/en.yml b/grocery-api/config/locales/en.yml
new file mode 100644
index 0000000..0653957
--- /dev/null
+++ b/grocery-api/config/locales/en.yml
@@ -0,0 +1,23 @@
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# To learn more, please read the Rails Internationalization guide
+# available at http://guides.rubyonrails.org/i18n.html.
+
+en:
+ hello: "Hello world"
diff --git a/grocery-api/config/routes.rb b/grocery-api/config/routes.rb
new file mode 100644
index 0000000..13b3fbb
--- /dev/null
+++ b/grocery-api/config/routes.rb
@@ -0,0 +1,60 @@
+Rails.application.routes.draw do
+ #get 'foods/index'
+
+ resources :foods, only: [:index]
+
+ # The priority is based upon order of creation: first created -> highest priority.
+ # See how all your routes lay out with "rake routes".
+
+ # You can have the root of your site routed with "root"
+ # root 'welcome#index'
+
+ # Example of regular route:
+ # get 'products/:id' => 'catalog#view'
+
+ # Example of named route that can be invoked with purchase_url(id: product.id)
+ # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
+
+ # Example resource route (maps HTTP verbs to controller actions automatically):
+ # resources :products
+
+ # Example resource route with options:
+ # resources :products do
+ # member do
+ # get 'short'
+ # post 'toggle'
+ # end
+ #
+ # collection do
+ # get 'sold'
+ # end
+ # end
+
+ # Example resource route with sub-resources:
+ # resources :products do
+ # resources :comments, :sales
+ # resource :seller
+ # end
+
+ # Example resource route with more complex sub-resources:
+ # resources :products do
+ # resources :comments
+ # resources :sales do
+ # get 'recent', on: :collection
+ # end
+ # end
+
+ # Example resource route with concerns:
+ # concern :toggleable do
+ # post 'toggle'
+ # end
+ # resources :posts, concerns: :toggleable
+ # resources :photos, concerns: :toggleable
+
+ # Example resource route within a namespace:
+ # namespace :admin do
+ # # Directs /admin/products/* to Admin::ProductsController
+ # # (app/controllers/admin/products_controller.rb)
+ # resources :products
+ # end
+end
diff --git a/grocery-api/config/secrets.yml b/grocery-api/config/secrets.yml
new file mode 100644
index 0000000..cad3991
--- /dev/null
+++ b/grocery-api/config/secrets.yml
@@ -0,0 +1,22 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key is used for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+# You can use `rake secret` to generate a secure secret key.
+
+# Make sure the secrets in this file are kept private
+# if you're sharing your code publicly.
+
+development:
+ secret_key_base: 89c0d0b93551c4843a7b893907344e91dd3ec4410622e084e1247b2555ddacb34a0b581d4ab567d665e585bba88be927f04e2bf1b98b52d8e2181d597e74d945
+
+test:
+ secret_key_base: 48ae636823ab7e0874900f0b1344b47ea7fe18189c25319ae3c82d2ed07535c4918d451162d349f45d3b6d583a974b0949a0ba178363dd449705fa7d565b37c2
+
+# Do not keep production secrets in the repository,
+# instead read values from the environment.
+production:
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
diff --git a/grocery-api/db/migrate/20160215224837_create_foods.rb b/grocery-api/db/migrate/20160215224837_create_foods.rb
new file mode 100644
index 0000000..2f766ed
--- /dev/null
+++ b/grocery-api/db/migrate/20160215224837_create_foods.rb
@@ -0,0 +1,10 @@
+class CreateFoods < ActiveRecord::Migration
+ def change
+ create_table :foods do |t|
+ t.string :name
+ t.integer :duration_seconds
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/grocery-api/db/schema.rb b/grocery-api/db/schema.rb
new file mode 100644
index 0000000..c0565b8
--- /dev/null
+++ b/grocery-api/db/schema.rb
@@ -0,0 +1,23 @@
+# encoding: UTF-8
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended that you check this file into your version control system.
+
+ActiveRecord::Schema.define(version: 20160215224837) do
+
+ create_table "foods", force: :cascade do |t|
+ t.string "name"
+ t.integer "duration_seconds"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+end
diff --git a/grocery-api/db/seeds.rb b/grocery-api/db/seeds.rb
new file mode 100644
index 0000000..6f28840
--- /dev/null
+++ b/grocery-api/db/seeds.rb
@@ -0,0 +1,17 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
+# Mayor.create(name: 'Emanuel', city: cities.first)
+
+Food.delete_all
+
+Food.create!([
+ {id: 1, name: "Banana", duration_seconds: 432000},
+ {id: 2, name: "Orange", duration_seconds: 604800},
+ {id: 3, name: "Apple", duration_seconds: 172800},
+ {id: 4, name: "Kale", duration_seconds: 604800},
+ {id: 5, name: "Spinach", duration_seconds: 604800}
+])
\ No newline at end of file
diff --git a/grocery-api/lib/assets/.keep b/grocery-api/lib/assets/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/lib/tasks/.keep b/grocery-api/lib/tasks/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/log/.keep b/grocery-api/log/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/grocery-api/node_modules/heroku-cli-util/.jshintignore b/grocery-api/node_modules/heroku-cli-util/.jshintignore
new file mode 100644
index 0000000..7b2d046
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/.jshintignore
@@ -0,0 +1,2 @@
+node_modules
+lib/linewrap.js
diff --git a/grocery-api/node_modules/heroku-cli-util/.jshintrc b/grocery-api/node_modules/heroku-cli-util/.jshintrc
new file mode 100644
index 0000000..fa85849
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/.jshintrc
@@ -0,0 +1,38 @@
+{
+ "asi": false,
+ "bitwise": true,
+ "curly": false,
+ "eqeqeq": true,
+ "esnext": true,
+ "forin": true,
+ "freeze": true,
+ "immed": true,
+ "indent": 2,
+ "latedef": true,
+ "laxcomma": true,
+ "maxcomplexity": 10,
+ "maxdepth": 5,
+ "maxparams": 5,
+ "maxstatements": 25,
+ "multistr": true,
+ "newcap": true,
+ "noarg": true,
+ "node": true,
+ "noempty": true,
+ "nonbsp": true,
+ "nonew": true,
+ "trailing": true,
+ "undef": true,
+ "unused": true,
+ "globals": {
+ "cli": true,
+ "nock": true
+ },
+ "predef": [
+ "afterEach",
+ "beforeEach",
+ "describe",
+ "it",
+ "sinon"
+ ]
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/.npmignore b/grocery-api/node_modules/heroku-cli-util/.npmignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/.npmignore
@@ -0,0 +1 @@
+node_modules
diff --git a/grocery-api/node_modules/heroku-cli-util/LICENSE b/grocery-api/node_modules/heroku-cli-util/LICENSE
new file mode 100644
index 0000000..817adf1
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/LICENSE
@@ -0,0 +1,3 @@
+Copyright (c) 2015, Heroku
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/README.md b/grocery-api/node_modules/heroku-cli-util/README.md
new file mode 100644
index 0000000..0a819bb
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/README.md
@@ -0,0 +1,263 @@
+# heroku-cli-util
+
+[](https://circleci.com/gh/heroku/heroku-cli-util/tree/master)
+[](http://badge.fury.io/js/heroku-cli-util)
+[](./LICENSE)
+[](http://inch-ci.org/github/heroku/heroku-cli-util)
+
+Set of helpful CLI utilities
+
+## Installation
+
+```sh
+npm install heroku-cli-util --save
+```
+
+## Action
+
+```js
+let cli = require('heroku-cli-util');
+let promise = heroku.apps(appname).info();
+let app = yield cli.action('getting apps', promise);
+console.log(`app name: ${app.name}`);
+
+// getting apps... done
+// app name: appname
+```
+
+Note: to use `yield` you need to wrap this in a [co](https://github.com/tj/co) block.
+
+## Prompt
+
+Callback style
+
+```js
+let cli = require('heroku-cli-util');
+cli.prompt('email', {}, function (_, email) {
+ console.log(`your email is: ${email}`);
+});
+```
+
+Promise style
+
+```js
+let cli = require('heroku-cli-util');
+cli.prompt('email', {}).then(function (email) {
+ console.log(`your email is: ${email}`);
+});
+```
+
+Generator style (must be wrapped in a [co](https://github.com/tj/co) block)
+
+```js
+let cli = require('heroku-cli-util');
+let email = yield cli.prompt('email', {});
+console.log(`your email is: ${email}`);
+```
+
+**cli.prompt options**
+
+```js
+cli.prompt('email', {
+ mask: true, // mask input field after submitting
+ hide: true // mask characters while entering
+});
+```
+
+## Confirm App
+
+Supports the same async styles as `prompt()`. Errors if not confirmed.
+
+Basic
+
+```js
+let cli = require('heroku-cli-util');
+yield cli.confirmApp('appname', context.flags.confirm);
+
+// ! WARNING: Destructive Action
+// ! This command will affect the app appname
+// ! To proceed, type appname or re-run this command with --confirm appname
+
+> appname
+```
+
+Custom message
+
+```js
+let cli = require('heroku-cli-util');
+yield cli.confirmApp('appname', context.flags.confirm, 'foo');
+
+// ! foo
+// ! To proceed, type appname or re-run this command with --confirm appname
+
+> appname
+```
+
+## Errors
+
+```js
+let cli = require('heroku-cli-util');
+cli.error("App not found");
+// ! App not found
+```
+
+## Warnings
+
+```js
+let cli = require('heroku-cli-util');
+cli.warn("App not found");
+// ! App not found
+```
+
+## Dates
+
+```js
+let cli = require('heroku-cli-util');
+let d = new Date();
+console.log(cli.formatDate(d));
+// 2001-01-01T08:00:00.000Z
+```
+
+## Hush
+
+Use hush for verbose logging when `HEROKU_DEBUG=1`.
+
+```js
+let cli = require('heroku-cli-util');
+cli.hush('foo');
+// only prints if HEROKU_DEBUG is set
+```
+
+## Debug
+
+Pretty print an object.
+
+```js
+let cli = require('heroku-cli-util');
+cli.debug({foo: [1,2,3]});
+// { foo: [ 1, 2, 3 ] }
+```
+
+## Stylized output
+
+Pretty print a header and hash
+```js
+let cli = require('heroku-cli-util');
+cli.styledHeader("MyApp");
+cli.styledHash({name: "myapp", collaborators: ["user1@example.com", "user2@example.com"]});
+```
+
+Produces
+
+```
+=== MyApp
+Collaborators: user1@example.com
+ user1@example.com
+Name: myapp
+```
+
+## Table
+
+```js
+cli.table([
+ {app: 'first-app', language: 'ruby', dyno_count: 3},
+ {app: 'second-app', language: 'node', dyno_count: 2},
+], {
+ columns: [
+ {key: 'app'},
+ {key: 'dyno_count', label: 'Dyno Count'},
+ {key: 'language', format: language => cli.color.red(language)},
+ ]
+});
+```
+
+Produces:
+
+```
+app Dyno Count language
+────────── ────────── ────────
+first-app 3 ruby
+second-app 2 node
+```
+
+## Open Web Browser
+
+```js
+yield cli.open('https://github.com');
+```
+
+## Mocking
+
+Mock stdout and stderr by using `cli.log()` and `cli.error()`.
+
+```js
+let cli = require('heroku-cli-util');
+cli.log('message 1'); // prints 'message 1'
+cli.mockConsole();
+cli.log('message 2'); // prints nothing
+cli.stdout.should.eq('message 2\n');
+```
+
+## Command
+
+Used for initializing a plugin command.
+give you an auth'ed instance of `heroku-client` and cleanly handle API exceptions.
+
+It expects you to return a promise chain. This is usually done with [co](https://github.com/tj/co).
+
+```js
+let cli = require('heroku-cli-util');
+let co = require('co');
+module.exports.commands = [
+ {
+ topic: 'apps',
+ command: 'info',
+ needsAuth: true,
+ needsApp: true,
+ run: cli.command(function (context, heroku) {
+ return co(function* () {
+ let app = yield heroku.apps(context.app).info();
+ console.dir(app);
+ });
+ })
+ }
+];
+```
+
+With options:
+
+```js
+let cli = require('heroku-cli-util');
+let co = require('co');
+module.exports.commands = [
+ {
+ topic: 'apps',
+ command: 'info',
+ needsAuth: true,
+ needsApp: true,
+ run: cli.command(
+ {preauth: true},
+ function (context, heroku) {
+ return co(function* () {
+ let app = yield heroku.apps(context.app).info();
+ console.dir(app);
+ });
+ }
+ )
+ }
+];
+```
+
+If the command has a `two_factor` API error, it will ask the user for a 2fa code and retry.
+If you set `preauth: true` it will preauth against the current app instead of just setting the header on an app. (This is necessary if you need to do more than 1 API call that will require 2fa)
+
+## Tests
+
+```sh
+npm install
+npm test
+```
+
+## License
+
+ISC
diff --git a/grocery-api/node_modules/heroku-cli-util/circle.yml b/grocery-api/node_modules/heroku-cli-util/circle.yml
new file mode 100644
index 0000000..7d7725c
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/circle.yml
@@ -0,0 +1,3 @@
+machine:
+ node:
+ version: 5.0.0
diff --git a/grocery-api/node_modules/heroku-cli-util/index.js b/grocery-api/node_modules/heroku-cli-util/index.js
new file mode 100644
index 0000000..37f4163
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/index.js
@@ -0,0 +1,32 @@
+'use strict';
+var console = require('./lib/console');
+var errors = require('./lib/errors');
+var prompt = require('./lib/prompt');
+var styled = require('./lib/styled');
+
+exports.hush = console.hush;
+exports.log = console.log.bind(console);
+exports.formatDate = require('./lib/date').formatDate;
+exports.error = errors.error;
+exports.warn = errors.warn;
+exports.errorHandler = errors.errorHandler;
+exports.console = console;
+exports.yubikey = require('./lib/yubikey');
+exports.prompt = prompt.prompt;
+exports.confirmApp = prompt.confirmApp;
+exports.preauth = require('./lib/preauth');
+exports.command = require('./lib/command');
+exports.color = require('./lib/color');
+exports.action = require('./lib/action');
+exports.extend = require('./lib/extend');
+exports.debug = console.debug;
+exports.mockConsole = console.mock;
+exports.table = require('./lib/table');
+exports.stdout = '';
+exports.stderr = '';
+exports.styledHeader = styled.styledHeader;
+exports.styledObject = styled.styledObject;
+exports.styledHash = styled.styledObject;
+exports.styledNameValues = styled.styledNameValues;
+exports.rollbar = require('./lib/rollbar');
+exports.open = require('./lib/open');
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/action.js b/grocery-api/node_modules/heroku-cli-util/lib/action.js
new file mode 100644
index 0000000..29210db
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/action.js
@@ -0,0 +1,28 @@
+'use strict';
+
+let cli = require('..');
+
+function action (message, options, promise) {
+ if (options.then) {
+ let swap = promise;
+ promise = options;
+ options = swap;
+ }
+ options = options || {};
+ cli.console.writeError(message + '... ');
+ return promise.then(function (result) {
+ if (options.success !== false) {
+ cli.console.error(options.success || 'done');
+ }
+ return result;
+ }).catch(function (err) {
+ if (err.body && err.body.id === 'two_factor') {
+ cli.console.error(cli.color.yellow.bold('!'));
+ } else {
+ cli.console.error(cli.color.red.bold('!!!'));
+ }
+ throw err;
+ });
+}
+
+module.exports = action;
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/color.js b/grocery-api/node_modules/heroku-cli-util/lib/color.js
new file mode 100644
index 0000000..1d98ff6
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/color.js
@@ -0,0 +1,22 @@
+'use strict';
+
+let _ = require('lodash');
+let chalk = require('chalk');
+
+let colors = {
+ app: 'cyan',
+ attachment: 'green',
+ addon: 'magenta',
+};
+
+let definedColors = _.pickBy(colors, function(value) {
+ return typeof(chalk[value]) === 'function';
+});
+
+let boundColors = _.mapValues(definedColors, function(value) {
+ return function(s) {
+ return chalk[value](s);
+ };
+});
+
+module.exports = Object.assign(chalk, boundColors);
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/command.js b/grocery-api/node_modules/heroku-cli-util/lib/command.js
new file mode 100644
index 0000000..bd45d89
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/command.js
@@ -0,0 +1,146 @@
+'use strict';
+
+let co = require('co');
+let Heroku = require('heroku-client');
+let cli = require('..');
+let url = require('url');
+let spawn = require('child_process').spawn;
+let exec = require('child_process').exec;
+
+function apiMiddleware (response, cb) {
+ let warning = response.headers['warning-message'];
+ if (warning) cli.warn(warning);
+ cb();
+}
+
+function heroku (context) {
+ let host = context.apiUrl || 'https://api.heroku.com';
+ let opts = {
+ userAgent: context.version,
+ debug: context.debug,
+ debugHeaders: context.debugHeaders,
+ token: context.auth ? context.auth.password : null,
+ host: host,
+ headers: {},
+ rejectUnauthorized: !(process.env.HEROKU_SSL_VERIFY === 'disable' || host.endsWith('herokudev.com')),
+ middleware: apiMiddleware,
+ };
+ if (process.env.HEROKU_HEADERS) {
+ opts.headers = cli.extend(opts.headers, JSON.parse(process.env.HEROKU_HEADERS));
+ }
+ if (context.secondFactor) {
+ opts.headers = cli.extend(opts.headers, {'Heroku-Two-Factor-Code': context.secondFactor});
+ }
+ if (context.reason) {
+ opts.headers = cli.extend(opts.headers, {'X-Heroku-Sudo-Reason': context.reason});
+ }
+ return new Heroku(opts);
+}
+
+let httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
+
+function setupHttpProxy() {
+ cli.hush(`proxy set to ${httpsProxy}`);
+ let proxy = url.parse(httpsProxy);
+ process.env.HEROKU_HTTP_PROXY_HOST = proxy.hostname;
+ process.env.HEROKU_HTTP_PROXY_PORT = proxy.port;
+}
+
+function errHandlerOpts (rollbar, context) {
+ rollbar = rollbar || {};
+ let cmd = null;
+ if (context.command) cmd = context.command.command ? `${context.command.topic}:${context.command.command}` : context.command.topic;
+ return {
+ debug: context.debug,
+ dev: context.dev,
+ logPath: context.herokuDir ? context.herokuDir+'/error.log' : null,
+ rollbar: cli.rollbar(rollbar.cred, {
+ version: context.version,
+ context: cmd,
+ })
+ };
+}
+
+function login () {
+ return new Promise(function (fulfill, error) {
+ spawn('heroku', ['login'], {stdio: 'inherit'})
+ .on('close', function (e) {
+ if (e === 0) fulfill();
+ else error(new Error('Authorization failed.'));
+ });
+ });
+}
+
+function getNewAPIKey () {
+ return new Promise(function (fulfill, reject) {
+ exec('heroku auth:token', function (error, stdout, stderr) {
+ if (stderr) console.error(stderr);
+ if (error) reject(error);
+ fulfill(stdout.trim());
+ });
+ });
+}
+
+function relogin () {
+ if (process.env.HEROKU_API_KEY) {
+ cli.error(`API key is currently set by the HEROKU_API_KEY environment variable.
+Ensure this is set to a correct value or unset it to use the netrc file.`);
+ process.exit(1);
+ }
+ return login().then(getNewAPIKey);
+}
+
+function twoFactorPrompt(options, context) {
+ cli.yubikey.enable();
+ return cli.prompt('Two-factor code', {mask: true})
+ .then(function (secondFactor) {
+ cli.yubikey.disable();
+ return secondFactor;
+ })
+ .then(function (secondFactor) {
+ if (options.preauth) {
+ return cli.preauth(context.app, heroku(context), secondFactor);
+ } else {
+ context.secondFactor = secondFactor;
+ }
+ });
+}
+
+function reasonPrompt(context) {
+ return cli.prompt('Reason')
+ .then(function (reason) {
+ context.reason = reason;
+ });
+}
+
+module.exports = function command (options, fn) {
+ return function (context) {
+ if (typeof options === 'function') {
+ fn = options;
+ options = {};
+ }
+ if (httpsProxy) { setupHttpProxy(); }
+ cli.color.enabled = context.supportsColor;
+ let handleErr = cli.errorHandler(errHandlerOpts(options.rollbar, context));
+ let run = function () {
+ return co.wrap(fn)(context, heroku(context))
+ .catch(function (err) {
+ if (err && err.body && err.body.id === 'unauthorized') {
+ cli.error(err.body.message || 'Unauthorized');
+ relogin()
+ .then(apiKey => context.auth = {password: apiKey})
+ .then(run)
+ .catch(handleErr);
+ }
+ else if (err && err.body && err.body.id === 'sudo_reason_required') {
+ cli.warn(err.body.message);
+ reasonPrompt(context).then(run).catch(handleErr);
+ }
+ else if (err && err.body && err.body.id === 'two_factor') {
+ twoFactorPrompt(options, context).then(run).catch(handleErr);
+ } else throw err;
+ }).catch(handleErr);
+ };
+ return run();
+ };
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/console.js b/grocery-api/node_modules/heroku-cli-util/lib/console.js
new file mode 100644
index 0000000..cd80d82
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/console.js
@@ -0,0 +1,99 @@
+'use strict';
+
+var cli = require('..');
+
+var mocking;
+
+function concatArguments(args) {
+ return Array.prototype.map.call(args, function(arg) {
+ return arg + '';
+ }).join(' ');
+}
+
+/**
+ * log is a wrapper for console.log() but can be mocked
+ *
+ * @param {...Object} obj - objects to be printed to stdout
+ */
+function log () {
+ if (mocking) {
+ cli.stdout += concatArguments(arguments) + '\n';
+ } else {
+ console.log.apply(null, arguments);
+ }
+}
+
+/**
+ * writeLog is a wrapper for process.stdout.write() but can be mocked
+ *
+ * @param {...Object} obj - objects to be printed to stdout
+ */
+function writeLog () {
+ if (mocking) {
+ cli.stdout += concatArguments(arguments);
+ } else {
+ process.stdout.write.apply(process.stdout, arguments);
+ }
+}
+
+function hush () {
+ let debug = process.env.HEROKU_DEBUG;
+ if (debug && (debug === '1' || debug.toUpperCase() === 'TRUE')) {
+ console.error.apply(null, arguments);
+ }
+}
+
+/**
+ * error is a wrapper for console.error() but can be mocked
+ *
+ * @param {...Object} obj - objects to be printed to stderr
+ */
+function error () {
+ if (mocking) {
+ cli.stderr += concatArguments(arguments) + '\n';
+ } else {
+ console.error.apply(null, arguments);
+ }
+}
+
+/**
+ * writeError is a wrapper for process.stderr.write() but can be mocked
+ *
+ * @param {...Object} obj - objects to be printed to stderr
+ */
+function writeError () {
+ if (mocking) {
+ cli.stderr += concatArguments(arguments);
+ } else {
+ process.stderr.write.apply(process.stderr, arguments);
+ }
+}
+
+/**
+ * mock will make {@link log} and {@link error}
+ * stop printing to stdout and stderr and start writing to the
+ * stdout and stderr strings.
+ */
+function mock () {
+ mocking = true;
+ cli.stderr = '';
+ cli.stdout = '';
+}
+
+/**
+ * debug pretty prints an object.
+ * It simply calls console.dir with color enabled.
+ *
+ * @param {Object} obj - object to be printed
+ */
+function debug (obj) {
+ console.dir(obj, {colors: true});
+}
+
+exports.hush = hush;
+exports.log = log;
+exports.writeLog = writeLog;
+exports.error = error;
+exports.writeError = writeError;
+exports.mock = mock;
+exports.debug = debug;
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/date.js b/grocery-api/node_modules/heroku-cli-util/lib/date.js
new file mode 100644
index 0000000..67d24d5
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/date.js
@@ -0,0 +1,18 @@
+'use strict';
+
+/**
+ * formatDate will format a date in a standard Heroku format
+ *
+ * @example
+ * let cli = require('heroku-cli-util');
+ * var d = new Date();
+ * cli.formatDate(d); // '2015-05-14T18:03:10.034Z'
+ *
+ * @param {Date} date the date to format
+ * @return {String} string representing the date
+ */
+function formatDate (date) {
+ return date.toISOString();
+}
+
+exports.formatDate = formatDate;
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/errors.js b/grocery-api/node_modules/heroku-cli-util/lib/errors.js
new file mode 100644
index 0000000..930ebed
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/errors.js
@@ -0,0 +1,102 @@
+'use strict';
+
+let cli = require('..');
+let console = require('./console');
+let linewrap = require('./linewrap');
+
+function errtermwidth() {
+ if (!process.stderr.isTTY) return 80;
+ return process.stderr.getWindowSize()[0];
+}
+
+function wrap (msg) {
+ return linewrap(6,
+ errtermwidth(), {
+ skipScheme: 'ansi-color'
+ })(msg || '');
+}
+
+function bangify (msg, c) {
+ let lines = msg.split('\n');
+ for(let i=0; i]*)>/gi;
+var lineBreakSchemeMap = {
+ 'unix': [/\n/g, '\n'],
+ 'dos': [/\r\n/g, '\r\n'],
+ 'mac': [/\r/g, '\r'],
+ 'html': [brPat, '
'],
+ 'xhtml': [brPat, '
']
+};
+
+// skip Schemes
+var skipSchemeMap = {
+ 'ansi-color': /\x1B\[[^m]*m/g,
+ 'html': /<[^>]*>/g,
+ 'bbcode': /\[[^]]*\]/g
+};
+
+var modeMap = {
+ 'soft': 1,
+ 'hard': 1
+};
+
+var wsMap = {
+ 'collapse': 1,
+ 'default': 1,
+ 'line': 1,
+ 'all': 1
+};
+
+var rlbMap = {
+ 'all': 1,
+ 'multi': 1,
+ 'none': 1
+};
+var rlbSMPat = /([sm])(\d+)/;
+
+var escapePat = /[-/\\^$*+?.()|[\]{}]/g;
+function escapeRegExp(s) {
+ return s.replace(escapePat, '\\$&');
+}
+
+var linewrap = module.exports = function (start, stop, params) {
+ if (typeof start === 'object') {
+ params = start;
+ start = params.start;
+ stop = params.stop;
+ }
+
+ if (typeof stop === 'object') {
+ params = stop;
+ start = start || params.start;
+ stop = undefined;
+ }
+
+ if (!stop) {
+ stop = start;
+ start = 0;
+ }
+
+ if (!params) { params = {}; }
+ // Supported options and default values.
+ var preset,
+ mode = 'soft',
+ whitespace = 'default',
+ tabWidth = 4,
+ skip, skipScheme, lineBreak, lineBreakScheme,
+ respectLineBreaks = 'all',
+ respectNum,
+ preservedLineIndent,
+ wrapLineIndent, wrapLineIndentBase;
+
+ var skipPat;
+ var lineBreakPat, lineBreakStr;
+ var multiLineBreakPat;
+ var preservedLinePrefix = '';
+ var wrapLineIndentPat, wrapLineInitPrefix = '';
+ var tabRepl;
+ var item, flags;
+ var i;
+
+ // First process presets, because these settings can be overwritten later.
+ preset = params.preset;
+ if (preset) {
+ if (!(preset instanceof Array)) {
+ preset = [preset];
+ }
+ for (i = 0; i < preset.length; i++) {
+ item = presetMap[preset[i]];
+ if (item) {
+ if (item.mode) {
+ mode = item.mode;
+ }
+ if (item.whitespace) {
+ whitespace = item.whitespace;
+ }
+ if (item.tabWidth !== undefined) {
+ tabWidth = item.tabWidth;
+ }
+ if (item.skip) {
+ skip = item.skip;
+ }
+ if (item.skipScheme) {
+ skipScheme = item.skipScheme;
+ }
+ if (item.lineBreak) {
+ lineBreak = item.lineBreak;
+ }
+ if (item.lineBreakScheme) {
+ lineBreakScheme = item.lineBreakScheme;
+ }
+ if (item.respectLineBreaks) {
+ respectLineBreaks = item.respectLineBreaks;
+ }
+ if (item.preservedLineIndent !== undefined) {
+ preservedLineIndent = item.preservedLineIndent;
+ }
+ if (item.wrapLineIndent !== undefined) {
+ wrapLineIndent = item.wrapLineIndent;
+ }
+ if (item.wrapLineIndentBase) {
+ wrapLineIndentBase = item.wrapLineIndentBase;
+ }
+ } else {
+ throw new TypeError('preset must be one of "' + Object.keys(presetMap).join('", "') + '"');
+ }
+ }
+ }
+
+ if (params.mode) {
+ if (modeMap[params.mode]) {
+ mode = params.mode;
+ } else {
+ throw new TypeError('mode must be one of "' + Object.keys(modeMap).join('", "') + '"');
+ }
+ }
+ // Available options: 'collapse', 'default', 'line', and 'all'
+ if (params.whitespace) {
+ if (wsMap[params.whitespace]) {
+ whitespace = params.whitespace;
+ } else {
+ throw new TypeError('whitespace must be one of "' + Object.keys(wsMap).join('", "') + '"');
+ }
+ }
+
+ if (params.tabWidth !== undefined) {
+ if (parseInt(params.tabWidth, 10) >= 0) {
+ tabWidth = parseInt(params.tabWidth, 10);
+ } else {
+ throw new TypeError('tabWidth must be a non-negative integer');
+ }
+ }
+ tabRepl = new Array(tabWidth + 1).join(' ');
+
+ // Available options: 'all', 'multi', 'm\d+', 's\d+', 'none'
+ if (params.respectLineBreaks) {
+ if (rlbMap[params.respectLineBreaks] || rlbSMPat.test(params.respectLineBreaks)) {
+ respectLineBreaks = params.respectLineBreaks;
+ } else {
+ throw new TypeError('respectLineBreaks must be one of "' + Object.keys(rlbMap).join('", "') +
+ '", "m", "s"');
+ }
+ }
+ // After these conversions, now we have 4 options in `respectLineBreaks`:
+ // 'all', 'none', 'm' and 's'.
+ // `respectNum` is applicable iff `respectLineBreaks` is either 'm' or 's'.
+ if (respectLineBreaks === 'multi') {
+ respectLineBreaks = 'm';
+ respectNum = 2;
+ } else if (!rlbMap[respectLineBreaks]) {
+ var match = rlbSMPat.exec(respectLineBreaks);
+ respectLineBreaks = match[1];
+ respectNum = parseInt(match[2], 10);
+ }
+
+ if (params.preservedLineIndent !== undefined) {
+ if (parseInt(params.preservedLineIndent, 10) >= 0) {
+ preservedLineIndent = parseInt(params.preservedLineIndent, 10);
+ } else {
+ throw new TypeError('preservedLineIndent must be a non-negative integer');
+ }
+ }
+
+ if (preservedLineIndent > 0) {
+ preservedLinePrefix = new Array(preservedLineIndent + 1).join(' ');
+ }
+
+ if (params.wrapLineIndent !== undefined) {
+ if (!isNaN(parseInt(params.wrapLineIndent, 10))) {
+ wrapLineIndent = parseInt(params.wrapLineIndent, 10);
+ } else {
+ throw new TypeError('wrapLineIndent must be an integer');
+ }
+ }
+ if (params.wrapLineIndentBase) {
+ wrapLineIndentBase = params.wrapLineIndentBase;
+ }
+
+ if (wrapLineIndentBase) {
+ if (wrapLineIndent === undefined) {
+ throw new TypeError('wrapLineIndent must be specified when wrapLineIndentBase is specified');
+ }
+ if (wrapLineIndentBase instanceof RegExp) {
+ wrapLineIndentPat = wrapLineIndentBase;
+ } else if (typeof wrapLineIndentBase === 'string') {
+ wrapLineIndentPat = new RegExp(escapeRegExp(wrapLineIndentBase));
+ } else {
+ throw new TypeError('wrapLineIndentBase must be either a RegExp object or a string');
+ }
+ } else if (wrapLineIndent > 0) {
+ wrapLineInitPrefix = new Array(wrapLineIndent + 1).join(' ');
+ } else if (wrapLineIndent < 0) {
+ throw new TypeError('wrapLineIndent must be non-negative when a base is not specified');
+ }
+
+ // NOTE: For the two RegExps `skipPat` and `lineBreakPat` that can be specified
+ // by the user:
+ // 1. We require them to be "global", so we have to convert them to global
+ // if the user specifies a non-global regex.
+ // 2. We cannot call `split()` on them, because they may or may not contain
+ // capturing parentheses which affect the output of `split()`.
+
+ // Precedence: Regex = Str > Scheme
+ if (params.skipScheme) {
+ if (skipSchemeMap[params.skipScheme]) {
+ skipScheme = params.skipScheme;
+ } else {
+ throw new TypeError('skipScheme must be one of "' + Object.keys(skipSchemeMap).join('", "') + '"');
+ }
+ }
+ if (params.skip) {
+ skip = params.skip;
+ }
+
+ if (skip) {
+ if (skip instanceof RegExp) {
+ skipPat = skip;
+ if (!skipPat.global) {
+ flags = 'g';
+ if (skipPat.ignoreCase) { flags += 'i'; }
+ if (skipPat.multiline) { flags += 'm'; }
+ skipPat = new RegExp(skipPat.source, flags);
+ }
+ } else if (typeof skip === 'string') {
+ skipPat = new RegExp(escapeRegExp(skip), 'g');
+ } else {
+ throw new TypeError('skip must be either a RegExp object or a string');
+ }
+ }
+ if (!skipPat && skipScheme) {
+ skipPat = skipSchemeMap[skipScheme];
+ }
+
+ // Precedence:
+ // - for lineBreakPat: Regex > Scheme > Str
+ // - for lineBreakStr: Str > Scheme > Regex
+ if (params.lineBreakScheme) {
+ if (lineBreakSchemeMap[params.lineBreakScheme]) {
+ lineBreakScheme = params.lineBreakScheme;
+ } else {
+ throw new TypeError('lineBreakScheme must be one of "' + Object.keys(lineBreakSchemeMap).join('", "') + '"');
+ }
+ }
+ if (params.lineBreak) {
+ lineBreak = params.lineBreak;
+ }
+
+ if (lineBreakScheme) {
+ // Supported schemes: 'unix', 'dos', 'mac', 'html', 'xhtml'
+ item = lineBreakSchemeMap[lineBreakScheme];
+ if (item) {
+ lineBreakPat = item[0];
+ lineBreakStr = item[1];
+ }
+ }
+ if (lineBreak) {
+ if (lineBreak instanceof Array) {
+ if (lineBreak.length === 1) {
+ lineBreak = lineBreak[0];
+ } else if (lineBreak.length >= 2) {
+ if (lineBreak[0] instanceof RegExp) {
+ lineBreakPat = lineBreak[0];
+ if (typeof lineBreak[1] === 'string') {
+ lineBreakStr = lineBreak[1];
+ }
+ } else if (lineBreak[1] instanceof RegExp) {
+ lineBreakPat = lineBreak[1];
+ if (typeof lineBreak[0] === 'string') {
+ lineBreakStr = lineBreak[0];
+ }
+ } else if (typeof lineBreak[0] === 'string' && typeof lineBreak[1] === 'string') {
+ lineBreakPat = new RegExp(escapeRegExp(lineBreak[0]), 'g');
+ lineBreakStr = lineBreak[1];
+ } else {
+ lineBreak = lineBreak[0];
+ }
+ }
+ }
+ if (typeof lineBreak === 'string') {
+ lineBreakStr = lineBreak;
+ if (!lineBreakPat) {
+ lineBreakPat = new RegExp(escapeRegExp(lineBreak), 'g');
+ }
+ } else if (lineBreak instanceof RegExp) {
+ lineBreakPat = lineBreak;
+ } else if (!(lineBreak instanceof Array)) {
+ throw new TypeError('lineBreak must be a RegExp object, a string, or an array consisted of a RegExp object and a string');
+ }
+ }
+ // Only assign defaults when `lineBreakPat` is not assigned.
+ // So if `params.lineBreak` is a RegExp, we don't have a value in `lineBreakStr`
+ // yet. We will try to get the value from the input string, and if failed, we
+ // will throw an exception.
+ if (!lineBreakPat) {
+ lineBreakPat = /\n/g;
+ lineBreakStr = '\n';
+ }
+
+ // Create `multiLineBreakPat` based on `lineBreakPat`, that matches strings
+ // consisted of one or more line breaks and zero or more whitespaces.
+ // Also convert `lineBreakPat` to global if not already so.
+ flags = 'g';
+ if (lineBreakPat.ignoreCase) { flags += 'i'; }
+ if (lineBreakPat.multiline) { flags += 'm'; }
+ multiLineBreakPat = new RegExp('\\s*(?:' + lineBreakPat.source + ')(?:' +
+ lineBreakPat.source + '|\\s)*', flags);
+ if (!lineBreakPat.global) {
+ lineBreakPat = new RegExp(lineBreakPat.source, flags);
+ }
+
+ // Initialize other useful variables.
+ var re = mode === 'hard' ? /\b/ : /(\S+\s+)/;
+ var prefix = new Array(start + 1).join(' ');
+ var wsStrip = (whitespace === 'default' || whitespace === 'collapse'),
+ wsCollapse = (whitespace === 'collapse'),
+ wsLine = (whitespace === 'line'),
+ wsAll = (whitespace === 'all');
+ var tabPat = /\t/g,
+ collapsePat = / +/g,
+ pPat = /^\s+/,
+ tPat = /\s+$/,
+ nonWsPat = /\S/,
+ wsPat = /\s/;
+ var wrapLen = stop - start;
+
+ return function (text) {
+ text = text.toString().replace(tabPat, tabRepl);
+
+ var match;
+ if (!lineBreakStr) {
+ // Try to get lineBreakStr from `text`
+ lineBreakPat.lastIndex = 0;
+ match = lineBreakPat.exec(text);
+ if (match) {
+ lineBreakStr = match[0];
+ } else {
+ throw new TypeError('Line break string for the output not specified');
+ }
+ }
+
+ // text -> blocks; each bloc -> segments; each segment -> chunks
+ var blocks, base = 0;
+ var mo, arr, b, res;
+ // Split `text` by line breaks.
+ blocks = [];
+ multiLineBreakPat.lastIndex = 0;
+ match = multiLineBreakPat.exec(text);
+ while(match) {
+ blocks.push(text.substring(base, match.index));
+
+ if (respectLineBreaks !== 'none') {
+ arr = [];
+ b = 0;
+ lineBreakPat.lastIndex = 0;
+ mo = lineBreakPat.exec(match[0]);
+ while(mo) {
+ arr.push(match[0].substring(b, mo.index));
+ b = mo.index + mo[0].length;
+ mo = lineBreakPat.exec(match[0]);
+ }
+ arr.push(match[0].substring(b));
+ blocks.push({type: 'break', breaks: arr});
+ } else {
+ // Strip line breaks and insert spaces when necessary.
+ if (wsCollapse) {
+ res = ' ';
+ } else {
+ res = match[0].replace(lineBreakPat, '');
+ }
+ blocks.push({type: 'break', remaining: res});
+ }
+
+ base = match.index + match[0].length;
+ match = multiLineBreakPat.exec(text);
+ }
+ blocks.push(text.substring(base));
+
+ var i, j, k;
+ var segments;
+ if (skipPat) {
+ segments = [];
+ for (i = 0; i < blocks.length; i++) {
+ var bloc = blocks[i];
+ if (typeof bloc !== 'string') {
+ // This is an object.
+ segments.push(bloc);
+ } else {
+ base = 0;
+ skipPat.lastIndex = 0;
+ match = skipPat.exec(bloc);
+ while(match) {
+ segments.push(bloc.substring(base, match.index));
+ segments.push({type: 'skip', value: match[0]});
+ base = match.index + match[0].length;
+ match = skipPat.exec(bloc);
+ }
+ segments.push(bloc.substring(base));
+ }
+ }
+ } else {
+ segments = blocks;
+ }
+
+ var chunks = [];
+ for (i = 0; i < segments.length; i++) {
+ var segment = segments[i];
+ if (typeof segment !== 'string') {
+ // This is an object.
+ chunks.push(segment);
+ } else {
+ if (wsCollapse) {
+ segment = segment.replace(collapsePat, ' ');
+ }
+
+ var parts = segment.split(re),
+ acc = [];
+
+ for (j = 0; j < parts.length; j++) {
+ var x = parts[j];
+ if (mode === 'hard') {
+ for (k = 0; k < x.length; k += wrapLen) {
+ acc.push(x.slice(k, k + wrapLen));
+ }
+ }
+ else { acc.push(x); }
+ }
+ chunks = chunks.concat(acc);
+ }
+ }
+
+ var curLine = 0,
+ curLineLength = start + preservedLinePrefix.length,
+ lines = [ prefix + preservedLinePrefix ],
+ // Holds the "real length" (excluding trailing whitespaces) of the
+ // current line if it exceeds `stop`, otherwise 0.
+ // ONLY USED when `wsAll` is true, in `finishOffCurLine()`.
+ bulge = 0,
+ // `cleanLine` is true iff we are at the beginning of an output line. By
+ // "beginning" we mean it doesn't contain any non-whitespace char yet.
+ // But its `curLineLength` can be greater than `start`, or even possibly
+ // be greater than `stop`, if `wsStrip` is false.
+ //
+ // Note that a "clean" line can still contain skip strings, in addition
+ // to whitespaces.
+ //
+ // This variable is used to allow us strip preceding whitespaces when
+ // `wsStrip` is true, or `wsLine` is true and `preservedLine` is false.
+ cleanLine = true,
+ // `preservedLine` is true iff we are in a preserved input line.
+ //
+ // It's used when `wsLine` is true to (combined with `cleanLine`) decide
+ // whether a whitespace is at the beginning of a preserved input line and
+ // should not be stripped.
+ preservedLine = true,
+ // The current indent prefix for wrapped lines.
+ wrapLinePrefix = wrapLineInitPrefix,
+ remnant;
+
+ // Always returns '' if `beforeHardBreak` is true.
+ //
+ // Assumption: Each call of this function is always followed by a `lines.push()` call.
+ //
+ // This function can change the status of `cleanLine`, but we don't modify the value of
+ // `cleanLine` in this function. It's fine because `cleanLine` will be set to the correct
+ // value after the `lines.push()` call following this function call. We also don't update
+ // `curLineLength` when pushing a new line and it's safe for the same reason.
+ function finishOffCurLine(beforeHardBreak) {
+ var str = lines[curLine],
+ idx, ln, rBase;
+
+ if (!wsAll) {
+ // Strip all trailing whitespaces past `start`.
+ idx = str.length - 1;
+ while (idx >= start && str[idx] === ' ') { idx--; }
+ while (idx >= start && wsPat.test(str[idx])) { idx--; }
+ idx++;
+
+ if (idx !== str.length) {
+ lines[curLine] = str.substring(0, idx);
+ }
+
+ if (preservedLine && cleanLine && wsLine && curLineLength > stop) {
+ // Add the remnants to the next line, just like when `wsAll` is true.
+ rBase = str.length - (curLineLength - stop);
+ if (rBase < idx) {
+ // We didn't reach `stop` when stripping due to a bulge.
+ rBase = idx;
+ }
+ }
+ } else {
+ // Strip trailing whitespaces exceeding stop.
+ if (curLineLength > stop) {
+ bulge = bulge || stop;
+ rBase = str.length - (curLineLength - bulge);
+ lines[curLine] = str.substring(0, rBase);
+ }
+ bulge = 0;
+ }
+
+ // Bug: the current implementation of `wrapLineIndent` is buggy: we are not
+ // taking the extra space occupied by the additional indentation into account
+ // when wrapping the line. For example, in "hard" mode, we should hard-wrap
+ // long words at `wrapLen - wrapLinePrefix.length` instead of `wrapLen`;
+ // and remnants should also be wrapped at `wrapLen - wrapLinePrefix.length`.
+ if (preservedLine) {
+ // This is a preserved line, and the next output line isn't a
+ // preserved line.
+ preservedLine = false;
+ if (wrapLineIndentPat) {
+ idx = lines[curLine].substring(start).search(wrapLineIndentPat);
+ if (idx >= 0 && idx + wrapLineIndent > 0) {
+ wrapLinePrefix = new Array(idx + wrapLineIndent + 1).join(' ');
+ } else {
+ wrapLinePrefix = '';
+ }
+ }
+ }
+
+ // Some remnants are left to the next line.
+ if (rBase) {
+ while (rBase + wrapLen < str.length) {
+ if (wsAll) {
+ ln = str.substring(rBase, rBase + wrapLen);
+ lines.push(prefix + wrapLinePrefix + ln);
+ } else {
+ lines.push(prefix + wrapLinePrefix);
+ }
+ rBase += wrapLen;
+ curLine++;
+ }
+ if (beforeHardBreak) {
+ if (wsAll) {
+ ln = str.substring(rBase);
+ lines.push(prefix + wrapLinePrefix + ln);
+ } else {
+ lines.push(prefix + wrapLinePrefix);
+ }
+ curLine++;
+ } else {
+ ln = str.substring(rBase);
+ return wrapLinePrefix + ln;
+ }
+ }
+
+ return '';
+ }
+
+ for (i = 0; i < chunks.length; i++) {
+ var chunk = chunks[i];
+
+ if (chunk === '') { continue; }
+
+ if (typeof chunk !== 'string') {
+ if (chunk.type === 'break') {
+ // This is one or more line breaks.
+ // Each entry in `breaks` is just zero or more whitespaces.
+ if (respectLineBreaks !== 'none') {
+ // Note that if `whitespace` is "collapse", we still need
+ // to collapse whitespaces in entries of `breaks`.
+ var breaks = chunk.breaks;
+ var num = breaks.length - 1;
+
+ if (respectLineBreaks === 's') {
+ // This is the most complex scenario. We have to check
+ // the line breaks one by one.
+ for (j = 0; j < num; j++) {
+ if (breaks[j+1].length < respectNum) {
+ // This line break should be stripped.
+ if (wsCollapse) {
+ breaks[j+1] = ' ';
+ } else {
+ breaks[j+1] = breaks[j] + breaks[j+1];
+ }
+ } else {
+ // This line break should be preserved.
+ // First finish off the current line.
+ if (wsAll) {
+ lines[curLine] += breaks[j];
+ curLineLength += breaks[j].length;
+ }
+ finishOffCurLine(true);
+
+ lines.push(prefix + preservedLinePrefix);
+ curLine++;
+ curLineLength = start + preservedLinePrefix.length;
+
+ preservedLine = cleanLine = true;
+ }
+ }
+ // We are adding to either the existing line (if no line break
+ // is qualified for preservance) or a "new" line.
+ if (!cleanLine || wsAll || (wsLine && preservedLine)) {
+ if (wsCollapse || (!cleanLine && breaks[num] === '')) {
+ breaks[num] = ' ';
+ }
+ lines[curLine] += breaks[num];
+ curLineLength += breaks[num].length;
+ }
+ } else if (respectLineBreaks === 'm' && num < respectNum) {
+ // These line breaks should be stripped.
+ if (!cleanLine || wsAll || (wsLine && preservedLine)) {
+ if (wsCollapse) {
+ chunk = ' ';
+ } else {
+ chunk = breaks.join('');
+ if (!cleanLine && chunk === '') {
+ chunk = ' ';
+ }
+ }
+ lines[curLine] += chunk;
+ curLineLength += chunk.length;
+ }
+ } else { // 'all' || ('m' && num >= respectNum)
+ // These line breaks should be preserved.
+ if (wsStrip) {
+ // Finish off the current line.
+ finishOffCurLine(true);
+
+ for (j = 0; j < num; j++) {
+ lines.push(prefix + preservedLinePrefix);
+ curLine++;
+ }
+
+ curLineLength = start + preservedLinePrefix.length;
+ preservedLine = cleanLine = true;
+
+ } else {
+ if (wsAll || (preservedLine && cleanLine)) {
+ lines[curLine] += breaks[0];
+ curLineLength += breaks[0].length;
+ }
+
+ for (j = 0; j < num; j++) {
+ // Finish off the current line.
+ finishOffCurLine(true);
+
+ lines.push(prefix + preservedLinePrefix + breaks[j+1]);
+ curLine++;
+ curLineLength = start + preservedLinePrefix.length + breaks[j+1].length;
+
+ preservedLine = cleanLine = true;
+ }
+ }
+ }
+ } else {
+ // These line breaks should be stripped.
+ if (!cleanLine || wsAll || (wsLine && preservedLine)) {
+ chunk = chunk.remaining;
+
+ // Bug: If `wsAll` is true, `cleanLine` is false, and `chunk`
+ // is '', we insert a space to replace the line break. This
+ // space will be preserved even if we are at the end of an
+ // output line, which is wrong behavior. However, I'm not
+ // sure it's worth it to fix this edge case.
+ if (wsCollapse || (!cleanLine && chunk === '')) {
+ chunk = ' ';
+ }
+ lines[curLine] += chunk;
+ curLineLength += chunk.length;
+ }
+ }
+ } else if (chunk.type === 'skip') {
+ // This is a skip string.
+ // Assumption: skip strings don't end with whitespaces.
+ if (curLineLength > stop) {
+ remnant = finishOffCurLine(false);
+
+ lines.push(prefix + wrapLinePrefix);
+ curLine++;
+ curLineLength = start + wrapLinePrefix.length;
+
+ if (remnant) {
+ lines[curLine] += remnant;
+ curLineLength += remnant.length;
+ }
+
+ cleanLine = true;
+ }
+ lines[curLine] += chunk.value;
+ }
+ continue;
+ }
+
+ var chunk2;
+ while (1) {
+ chunk2 = undefined;
+ if (curLineLength + chunk.length > stop &&
+ curLineLength + (chunk2 = chunk.replace(tPat, '')).length > stop &&
+ chunk2 !== '' &&
+ curLineLength > start) {
+ // This line is full, add `chunk` to the next line
+ remnant = finishOffCurLine(false);
+
+ lines.push(prefix + wrapLinePrefix);
+ curLine++;
+ curLineLength = start + wrapLinePrefix.length;
+
+ if (remnant) {
+ lines[curLine] += remnant;
+ curLineLength += remnant.length;
+ cleanLine = true;
+ continue;
+ }
+
+ if (wsStrip || (wsLine && !(preservedLine && cleanLine))) {
+ chunk = chunk.replace(pPat, '');
+ }
+ cleanLine = false;
+
+ } else {
+ // Add `chunk` to this line
+ if (cleanLine) {
+ if (wsStrip || (wsLine && !(preservedLine && cleanLine))) {
+ chunk = chunk.replace(pPat, '');
+ if (chunk !== '') {
+ cleanLine = false;
+ }
+ } else {
+ if (nonWsPat.test(chunk)) {
+ cleanLine = false;
+ }
+ }
+ }
+ }
+ break;
+ }
+ if (wsAll && chunk2 && curLineLength + chunk2.length > stop) {
+ bulge = curLineLength + chunk2.length;
+ }
+ lines[curLine] += chunk;
+ curLineLength += chunk.length;
+ }
+ // Finally, finish off the last line.
+ finishOffCurLine(true);
+ return lines.join(lineBreakStr);
+ };
+};
+
+linewrap.soft = linewrap;
+
+linewrap.hard = function (/*start, stop, params*/) {
+ var args = [].slice.call(arguments);
+ var last = args.length - 1;
+ if (typeof args[last] === 'object') {
+ args[last].mode = 'hard';
+ } else {
+ args.push({ mode : 'hard' });
+ }
+ return linewrap.apply(null, args);
+};
+
+linewrap.wrap = function(text/*, start, stop, params*/) {
+ var args = [].slice.call(arguments);
+ args.shift();
+ return linewrap.apply(null, args)(text);
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/open.js b/grocery-api/node_modules/heroku-cli-util/lib/open.js
new file mode 100644
index 0000000..d57d75b
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/open.js
@@ -0,0 +1,19 @@
+'use strict';
+
+let color = require('./color');
+
+function open (url) {
+ let opn = require('opn');
+ return new Promise((fulfill, reject) => {
+ opn(url, {wait: false}, err => {
+ if (err) reject(new Error(
+`Error opening web browser.
+${err}
+
+Manually visit ${color.cyan(url)} in your browser.`));
+ else fulfill(err);
+ });
+ });
+}
+
+module.exports = open;
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/preauth.js b/grocery-api/node_modules/heroku-cli-util/lib/preauth.js
new file mode 100644
index 0000000..d91335e
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/preauth.js
@@ -0,0 +1,26 @@
+'use strict';
+let util = require('./util');
+
+/**
+ * preauth will make an API call to preauth a user for an app
+ * this makes it so the user will not have to enter a 2fa code
+ * for the next few minutes on the specified app.
+ *
+ * You need this if your command is going to make multiple API calls
+ * since otherwise the secondFactor key would only work one time for
+ * yubikeys.
+ *
+ * @param {String} app the app to preauth against
+ * @param {Heroku} heroku a heroku api client
+ * @param {String} secondFactor a second factor code
+ * @return {Promise} A promise fulfilled when the preauth is complete
+ */
+function preauth (app, heroku, secondFactor) {
+ return heroku.request({
+ method: 'PUT',
+ path: `/apps/${app}/pre-authorizations`,
+ headers: { 'Heroku-Two-Factor-Code': secondFactor }
+ });
+}
+
+module.exports = util.promiseOrCallback(preauth);
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/process.js b/grocery-api/node_modules/heroku-cli-util/lib/process.js
new file mode 100644
index 0000000..fd472e0
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/process.js
@@ -0,0 +1,14 @@
+function Process() {
+ this.env = process.env;
+}
+Process.prototype.mock = function () {
+ this.mocking = true;
+};
+Process.prototype.exit = function (code) {
+ if (this.mocking) {
+ this.exitCode = code;
+ } else {
+ process.exit(code);
+ }
+};
+module.exports = new Process();
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/prompt.js b/grocery-api/node_modules/heroku-cli-util/lib/prompt.js
new file mode 100644
index 0000000..1ec0014
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/prompt.js
@@ -0,0 +1,102 @@
+'use strict';
+
+let cli = require('../');
+let errors = require('./errors');
+let util = require('./util');
+
+function promptMasked (options) {
+ function refresh (input, mask) {
+ process.stderr.clearLine();
+ process.stderr.cursorTo(0);
+ process.stderr.write(options.prompt + (mask ? input.replace(/./g, '*') : input));
+ }
+
+ function start () {
+ process.stdin.setEncoding('utf8');
+ process.stderr.write(options.prompt);
+ process.stdin.resume();
+ if (!process.stdin.isTTY) {
+ throw new Error(`need to prompt for ${options.name || options.prompt} but stdin is not a tty`);
+ }
+ process.stdin.setRawMode(true);
+ }
+
+ function stop () {
+ console.error();
+ process.stdin.setRawMode(false);
+ process.stdin.pause();
+ }
+
+ return new Promise(function (fulfill, reject) {
+ start();
+ let input = '';
+ let fn = function (c) {
+ switch(c) {
+ case "\u0004": // Ctrl-d
+ case "\r":
+ case "\n":
+ if (input.length === 0) return;
+ refresh(input, true);
+ stop();
+ process.stdin.removeListener('data', fn);
+ fulfill(input);
+ return;
+ case "\u0003":
+ // Ctrl-c
+ reject('');
+ stop();
+ process.stdin.removeListener('data', fn);
+ return;
+ default:
+ // backspace
+ if (c.charCodeAt(0) === 127) input = input.substr(0, input.length-1);
+ else input += c;
+ refresh(input, options.hide);
+ return;
+ }
+ };
+ process.stdin.on('data', fn);
+ });
+}
+
+function prompt (name, options) {
+ options = options || {};
+ options.prompt = name ? name + ': ' : '> ';
+ if (options.mask || options.hide) return promptMasked(options);
+ return new Promise(function (fulfill) {
+ process.stdin.setEncoding('utf8');
+ process.stderr.write(options.prompt);
+ process.stdin.resume();
+ process.stdin.once('data', function (data) {
+ process.stdin.pause();
+ data = data.trim();
+ if (data === '') {
+ fulfill(prompt(name));
+ } else {
+ fulfill(data);
+ }
+ });
+ });
+}
+
+function confirmApp (app, confirm, message) {
+ return new Promise(function (fulfill, reject) {
+ if (confirm) {
+ if (confirm === app) return fulfill();
+ return reject(`Confirmation ${cli.color.bold.red(confirm)} did not match ${cli.color.bold.red(app)}. Aborted.`);
+ }
+ if (!message) message = `WARNING: Destructive Action\nThis command will affect the app ${cli.color.bold.red(app)}`;
+ errors.warn(message);
+ errors.warn(`To proceed, type ${cli.color.bold.red(app)} or re-run this command with ${cli.color.bold.red('--confirm', app)}`);
+ console.error();
+ prompt().then(function (confirm) {
+ if (confirm === app) {
+ return fulfill();
+ }
+ return reject(`Confirmation did not match ${cli.color.bold.red(app)}. Aborted.`);
+ });
+ });
+}
+
+exports.prompt = util.promiseOrCallback(prompt);
+exports.confirmApp = util.promiseOrCallback(confirmApp);
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/rollbar.js b/grocery-api/node_modules/heroku-cli-util/lib/rollbar.js
new file mode 100644
index 0000000..f85ab40
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/rollbar.js
@@ -0,0 +1,80 @@
+'use strict';
+
+let https = require('https');
+let parseUrl = require('url').parse;
+
+const GLOBAL_CRED = 'b66ca6f60fe049d6bedfe3e2ccb28d8c';
+const ROLLBAR_URL = 'https://api.rollbar.com/api/1/item/';
+
+function concat (stream, callback) {
+ var strings = [];
+ stream.on('data', function (data) {
+ strings.push(data);
+ });
+ stream.on('end', function () {
+ callback(strings.join(''));
+ });
+}
+
+function request (method, url, payload) {
+ return new Promise(function (fulfill, reject) {
+ let body = JSON.stringify(payload);
+ url = parseUrl(url);
+ let req = https.request({
+ hostname: url.hostname,
+ path: url.path,
+ method: method
+ }, function (res) {
+ concat(res, function (data) {
+ if (res.statusCode >= 200 && res.statusCode < 300)
+ fulfill(data);
+ else
+ reject(data);
+ });
+ });
+ req.setHeader('Content-Length', Buffer.byteLength(body));
+ req.write(body);
+ });
+}
+
+module.exports = function (cred, opts) {
+ if (!cred) cred = GLOBAL_CRED;
+
+ function error (err) {
+ const regex = /^\s*at (?:([^(]+(?: \[\w\s+\])?) )?\(?(.+?)(?::(\d+):(\d+)(?:, :(\d+):(\d+))?)?\)?$/;
+ let frames = (err.stack || '').split('\n').map(function (line) {
+ let r = line.match(regex);
+ if (!r) { return; }
+ return {
+ method: r[1],
+ filename: r[2],
+ lineno: r[3],
+ colno: r[4]
+ };
+ });
+ frames.shift(); // discard first line
+ return request('POST', ROLLBAR_URL, {
+ access_token: cred,
+ data: {
+ code_version: opts.version,
+ context: opts.context,
+ platform: 'client',
+ environment: 'production',
+ framework: 'node',
+ body: {
+ trace: {
+ exception: {
+ class: err.name,
+ message: err.message || err,
+ },
+ frames
+ }
+ }
+ }
+ });
+ }
+
+ return {
+ error
+ };
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/styled.js b/grocery-api/node_modules/heroku-cli-util/lib/styled.js
new file mode 100644
index 0000000..79ced99
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/styled.js
@@ -0,0 +1,108 @@
+'use strict';
+
+let cli = require('..');
+let util = require('util');
+
+/**
+ * styledHeader logs in a consistent header style
+ *
+ * @example
+ * styledHeader('MyApp') # Outputs === MyApp
+ *
+ * @param {header} header text
+ * @returns {null}
+ */
+function styledHeader(header) {
+ cli.log(cli.color.gray('=== ') + cli.color.bold(header));
+}
+
+/**
+ * styledObject logs an object in a consistent columnar style
+ *
+ * @example
+ * styledObject({name: "myapp", collaborators: ["user1@example.com", "user2@example.com"]})
+ * Collaborators: user1@example.com
+ * user2@example.com
+ * Name: myapp
+ *
+ * @param {obj} object data to print
+ * @param {keys} optional array of keys to sort/filter output
+ * @returns {null}
+ */
+function styledObject(obj, keys) {
+ let keyLengths = Object.keys(obj).map(function(key) { return key.toString().length; });
+ let maxKeyLength = Math.max.apply(Math, keyLengths) + 2;
+ function pp(obj) {
+ if (typeof obj === 'string' || typeof obj === 'number') {
+ return obj;
+ } else if (typeof obj === 'object') {
+ return Object.keys(obj).map(k => k + ': ' + util.inspect(obj[k])).join(', ');
+ } else {
+ return util.inspect(obj);
+ }
+ }
+ function logKeyValue(key, value) {
+ cli.log(`${key}:`+' '.repeat(maxKeyLength - key.length-1)+pp(value));
+ }
+ for (var key of (keys || Object.keys(obj).sort())) {
+ let value = obj[key];
+ if (Array.isArray(value)) {
+ if (value.length > 0) {
+ logKeyValue(key, value[0]);
+ for (var e of value.slice(1)) {
+ cli.log(" ".repeat(maxKeyLength) + pp(e));
+ }
+ }
+ } else if (value !== null && value !== undefined) {
+ logKeyValue(key, value);
+ }
+ }
+}
+
+/**
+ * styledNameValues logs an array of {name: '', values: ['']} objects in a consistent style
+ *
+ * @example
+ * styledNameValues([{name: "Collaborators", values: ["user1@example.com", "user2@example.com"]},
+ * {name: "Name", values: ["myapp"]}])
+ * Collaborators: user1@example.com
+ * user2@example.com
+ * Name: myapp
+ *
+ * @param {nameValues} nameValues
+ * @returns {null}
+ */
+function styledNameValues(nameValues) {
+ let keys = nameValues.map(function(nameValue) { return nameValue.name; });
+ let keyLengths = keys.map(function(name) { return name.toString().length; });
+ let maxKeyLength = Math.max.apply(Math, keyLengths) + 2;
+ function pp(obj) {
+ if (typeof obj === 'string' || typeof obj === 'number') {
+ return obj;
+ } else if (typeof obj === 'object') {
+ return Object.keys(obj).map(k => k + ': ' + cli.inspect(obj[k])).join(', ');
+ } else {
+ return cli.inspect(obj);
+ }
+ }
+ function logKeyValue(key, value) {
+ cli.log(`${key}:`+' '.repeat(maxKeyLength - key.length-1)+pp(value));
+ }
+ for (var nameValue of nameValues) {
+ let value = nameValue.values;
+ if (Array.isArray(value)) {
+ if (value.length > 0) {
+ logKeyValue(nameValue.name, value[0]);
+ for (var e of value.slice(1)) {
+ cli.log(" ".repeat(maxKeyLength) + pp(e));
+ }
+ }
+ } else if (value !== null && value !== undefined) {
+ logKeyValue(nameValue.name, value);
+ }
+ }
+}
+
+module.exports.styledHeader = styledHeader;
+module.exports.styledObject = styledObject;
+module.exports.styledNameValues = styledNameValues;
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/table.js b/grocery-api/node_modules/heroku-cli-util/lib/table.js
new file mode 100644
index 0000000..a362062
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/table.js
@@ -0,0 +1,128 @@
+'use strict';
+
+let stripAnsi = require('..').color.stripColor;
+let _ = require('lodash');
+let cli = require('..');
+
+let defaults = {
+ colSep: ' ',
+ after: _.noop,
+ headerAnsi: _.identity,
+ printLine: cli.log,
+ printRow: function(cells) {
+ this.printLine(cells.join(this.colSep).trimRight());
+ },
+ printHeader: function(cells) {
+ this.printRow(cells.map(_.ary(this.headerAnsi, 1)));
+ this.printRow(cells.map(function(hdr) { return hdr.replace(/./g, '─'); }));
+ }
+};
+
+let colDefaults = {
+ format: value => value ? value.toString() : '',
+ width: 0,
+ label: function() { return this.key.toString(); },
+
+ get: function(row) {
+ let value;
+ let path = _.result(this, 'key');
+
+ if(!path) {
+ value = row;
+ } else {
+ value = _.get(row, path);
+ }
+
+ return this.format(value, row);
+ },
+};
+
+function calcWidth(cell) {
+ let lines = stripAnsi(cell).split(/[\r\n]+/);
+ let lineLengths = lines.map(_.property('length'));
+ return Math.max.apply(Math, lineLengths);
+}
+
+function pad(string, length) {
+ let visibleLength = stripAnsi(string).length;
+ let diff = length - visibleLength;
+
+ return string + _.repeat(' ', Math.max(0, diff));
+}
+
+/**
+ * Generates a Unicode table and feeds it into configured printer.
+ *
+ * Top-level arguments:
+ *
+ * @arg {Object[]} data - the records to format as a table.
+ * @arg {Object} options - configuration for the table.
+ *
+ * @arg {Object[]} [options.columns] - Options for formatting and finding values for table columns.
+ * @arg {function(string)} [options.headerAnsi] - Zero-width formattter for entire header.
+ * @arg {string} [options.colSep] - Separator between columns.
+ * @arg {function(row, options)} [options.after] - Function called after each row is printed.
+ * @arg {function(string)} [options.printLine] - Function responsible for printing to terminal.
+ * @arg {function(cells)} [options.printHeader] - Function to print header cells as a row.
+ * @arg {function(cells)} [options.printRow] - Function to print cells as a row.
+ *
+ * @arg {function(row)|string} [options.columns[].key] - Path to the value in the row or function to retrieve the pre-formatted value for the cell.
+ * @arg {function(string)} [options.columns[].label] - Header name for column.
+ * @arg {function(string, row)} [options.columns[].format] - Formatter function for column value.
+ * @arg {function(row)} [options.columns[].get] - Function to return a value to be presented in cell without formatting.
+ *
+ */
+function table(data, options) {
+ _.defaults(options, defaults);
+
+ let columns = options.columns || _.keys(data[0] || {});
+
+ if(typeof columns[0] === 'string') {
+ columns = columns.map(function(k) { return {key: k}; });
+ }
+
+ let defaultsApplied = false;
+ for(let row of data) {
+ row.height = 1;
+ for(let col of columns) {
+ if(!defaultsApplied) { _.defaults(col, colDefaults); }
+
+ let cell = col.get(row);
+
+ col.width = Math.max(
+ col.label.length,
+ col.width,
+ calcWidth(cell)
+ );
+
+ row.height = Math.max(
+ row.height,
+ cell.split(/[\r\n]+/).length
+ );
+ }
+ defaultsApplied = true;
+ }
+
+ if (options.printHeader) {
+ options.printHeader(columns.map(function(col) {
+ let label = _.result(col, 'label');
+ return pad(label, col.width || label.length );
+ }));
+ }
+
+ function getNthLineOfCell(n, row, col) {
+ // TODO memoize this
+ let lines = col.get(row).split(/[\r\n]+/);
+ return pad(lines[n] || '', col.width);
+ }
+
+ for(let row of data) {
+ for(let i = 0; i < row.height; i++) {
+ let cells = columns.map(_.partial(getNthLineOfCell, i, row));
+ options.printRow(cells);
+ }
+ options.after(row, options);
+ }
+}
+
+module.exports = table;
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/util.js b/grocery-api/node_modules/heroku-cli-util/lib/util.js
new file mode 100644
index 0000000..d084760
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/util.js
@@ -0,0 +1,38 @@
+'use strict';
+
+/**
+ * promiseOrCallback will convert a function that returns a promise
+ * into one that will either make a node-style callback or return a promise
+ * based on whether or not a callback is passed in.
+ *
+ * @example
+ * prompt('input? ').then(function (input) {
+ * // deal with input
+ * });
+ * var prompt2 = promiseOrCallback(prompt);
+ * prompt('input? ', function (err, input) {
+ * // deal with input
+ * });
+ *
+ * @param {Function} fn a promise returning function to wrap
+ * @returns {Function} a function that behaves like before unless called with a callback
+ */
+function promiseOrCallback(fn) {
+ return function () {
+ if (typeof arguments[arguments.length-1] === 'function') {
+ let args = Array.prototype.slice.call(arguments);
+ let callback = args.pop();
+ fn.apply(null, args).then(function () {
+ let args = Array.prototype.slice.call(arguments);
+ args.unshift(null);
+ callback.apply(null, args);
+ }).catch(function (err) {
+ callback.call(null, err);
+ });
+ } else {
+ return fn.apply(null, arguments);
+ }
+ };
+}
+
+exports.promiseOrCallback = promiseOrCallback;
diff --git a/grocery-api/node_modules/heroku-cli-util/lib/yubikey.js b/grocery-api/node_modules/heroku-cli-util/lib/yubikey.js
new file mode 100644
index 0000000..62d78db
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/lib/yubikey.js
@@ -0,0 +1,22 @@
+'use strict';
+
+let execSync = require('child_process').execSync;
+
+function enable () {
+ if (process.platform !== 'darwin') return;
+ try {
+ execSync(`osascript -e 'if application "yubiswitch" is running then tell application "yubiswitch" to KeyOn'`, {stdio: 'inherit'});
+ } catch (err) { }
+}
+
+function disable () {
+ if (process.platform !== 'darwin') return;
+ try {
+ execSync(`osascript -e 'if application "yubiswitch" is running then tell application "yubiswitch" to KeyOff'`, {stdio: 'inherit'});
+ } catch (err) { }
+}
+
+module.exports = {
+ enable,
+ disable,
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/index.js
new file mode 100644
index 0000000..2d85a91
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/index.js
@@ -0,0 +1,116 @@
+'use strict';
+var escapeStringRegexp = require('escape-string-regexp');
+var ansiStyles = require('ansi-styles');
+var stripAnsi = require('strip-ansi');
+var hasAnsi = require('has-ansi');
+var supportsColor = require('supports-color');
+var defineProps = Object.defineProperties;
+var isSimpleWindowsTerm = process.platform === 'win32' && !/^xterm/i.test(process.env.TERM);
+
+function Chalk(options) {
+ // detect mode if not set manually
+ this.enabled = !options || options.enabled === undefined ? supportsColor : options.enabled;
+}
+
+// use bright blue on Windows as the normal blue color is illegible
+if (isSimpleWindowsTerm) {
+ ansiStyles.blue.open = '\u001b[94m';
+}
+
+var styles = (function () {
+ var ret = {};
+
+ Object.keys(ansiStyles).forEach(function (key) {
+ ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
+
+ ret[key] = {
+ get: function () {
+ return build.call(this, this._styles.concat(key));
+ }
+ };
+ });
+
+ return ret;
+})();
+
+var proto = defineProps(function chalk() {}, styles);
+
+function build(_styles) {
+ var builder = function () {
+ return applyStyle.apply(builder, arguments);
+ };
+
+ builder._styles = _styles;
+ builder.enabled = this.enabled;
+ // __proto__ is used because we must return a function, but there is
+ // no way to create a function with a different prototype.
+ /* eslint-disable no-proto */
+ builder.__proto__ = proto;
+
+ return builder;
+}
+
+function applyStyle() {
+ // support varags, but simply cast to string in case there's only one arg
+ var args = arguments;
+ var argsLen = args.length;
+ var str = argsLen !== 0 && String(arguments[0]);
+
+ if (argsLen > 1) {
+ // don't slice `arguments`, it prevents v8 optimizations
+ for (var a = 1; a < argsLen; a++) {
+ str += ' ' + args[a];
+ }
+ }
+
+ if (!this.enabled || !str) {
+ return str;
+ }
+
+ var nestedStyles = this._styles;
+ var i = nestedStyles.length;
+
+ // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe,
+ // see https://github.com/chalk/chalk/issues/58
+ // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop.
+ var originalDim = ansiStyles.dim.open;
+ if (isSimpleWindowsTerm && (nestedStyles.indexOf('gray') !== -1 || nestedStyles.indexOf('grey') !== -1)) {
+ ansiStyles.dim.open = '';
+ }
+
+ while (i--) {
+ var code = ansiStyles[nestedStyles[i]];
+
+ // Replace any instances already present with a re-opening code
+ // otherwise only the part of the string until said closing code
+ // will be colored, and the rest will simply be 'plain'.
+ str = code.open + str.replace(code.closeRe, code.open) + code.close;
+ }
+
+ // Reset the original 'dim' if we changed it to work around the Windows dimmed gray issue.
+ ansiStyles.dim.open = originalDim;
+
+ return str;
+}
+
+function init() {
+ var ret = {};
+
+ Object.keys(styles).forEach(function (name) {
+ ret[name] = {
+ get: function () {
+ return build.call(this, [name]);
+ }
+ };
+ });
+
+ return ret;
+}
+
+defineProps(Chalk.prototype, init());
+
+module.exports = new Chalk();
+module.exports.styles = ansiStyles;
+module.exports.hasColor = hasAnsi;
+module.exports.stripColor = stripAnsi;
+module.exports.supportsColor = supportsColor;
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/index.js
new file mode 100644
index 0000000..7894527
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/index.js
@@ -0,0 +1,65 @@
+'use strict';
+
+function assembleStyles () {
+ var styles = {
+ modifiers: {
+ reset: [0, 0],
+ bold: [1, 22], // 21 isn't widely supported and 22 does the same thing
+ dim: [2, 22],
+ italic: [3, 23],
+ underline: [4, 24],
+ inverse: [7, 27],
+ hidden: [8, 28],
+ strikethrough: [9, 29]
+ },
+ colors: {
+ black: [30, 39],
+ red: [31, 39],
+ green: [32, 39],
+ yellow: [33, 39],
+ blue: [34, 39],
+ magenta: [35, 39],
+ cyan: [36, 39],
+ white: [37, 39],
+ gray: [90, 39]
+ },
+ bgColors: {
+ bgBlack: [40, 49],
+ bgRed: [41, 49],
+ bgGreen: [42, 49],
+ bgYellow: [43, 49],
+ bgBlue: [44, 49],
+ bgMagenta: [45, 49],
+ bgCyan: [46, 49],
+ bgWhite: [47, 49]
+ }
+ };
+
+ // fix humans
+ styles.colors.grey = styles.colors.gray;
+
+ Object.keys(styles).forEach(function (groupName) {
+ var group = styles[groupName];
+
+ Object.keys(group).forEach(function (styleName) {
+ var style = group[styleName];
+
+ styles[styleName] = group[styleName] = {
+ open: '\u001b[' + style[0] + 'm',
+ close: '\u001b[' + style[1] + 'm'
+ };
+ });
+
+ Object.defineProperty(styles, groupName, {
+ value: group,
+ enumerable: false
+ });
+ });
+
+ return styles;
+}
+
+Object.defineProperty(module, 'exports', {
+ enumerable: true,
+ get: assembleStyles
+});
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/package.json
new file mode 100644
index 0000000..b6a9cea
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/package.json
@@ -0,0 +1,80 @@
+{
+ "name": "ansi-styles",
+ "version": "2.1.0",
+ "description": "ANSI escape codes for styling strings in the terminal",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/chalk/ansi-styles.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "18421cbe4a2d93359ec2599a894f704be126d066",
+ "bugs": {
+ "url": "https://github.com/chalk/ansi-styles/issues"
+ },
+ "homepage": "https://github.com/chalk/ansi-styles",
+ "_id": "ansi-styles@2.1.0",
+ "_shasum": "990f747146927b559a932bf92959163d60c0d0e2",
+ "_from": "ansi-styles@>=2.1.0 <3.0.0",
+ "_npmVersion": "2.10.1",
+ "_nodeVersion": "0.12.4",
+ "_npmUser": {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ "dist": {
+ "shasum": "990f747146927b559a932bf92959163d60c0d0e2",
+ "tarball": "http://registry.npmjs.org/ansi-styles/-/ansi-styles-2.1.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.1.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/readme.md
new file mode 100644
index 0000000..3f933f6
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/ansi-styles/readme.md
@@ -0,0 +1,86 @@
+# ansi-styles [](https://travis-ci.org/chalk/ansi-styles)
+
+> [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal
+
+You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings.
+
+
+
+
+## Install
+
+```
+$ npm install --save ansi-styles
+```
+
+
+## Usage
+
+```js
+var ansi = require('ansi-styles');
+
+console.log(ansi.green.open + 'Hello world!' + ansi.green.close);
+```
+
+
+## API
+
+Each style has an `open` and `close` property.
+
+
+## Styles
+
+### Modifiers
+
+- `reset`
+- `bold`
+- `dim`
+- `italic` *(not widely supported)*
+- `underline`
+- `inverse`
+- `hidden`
+- `strikethrough` *(not widely supported)*
+
+### Colors
+
+- `black`
+- `red`
+- `green`
+- `yellow`
+- `blue`
+- `magenta`
+- `cyan`
+- `white`
+- `gray`
+
+### Background colors
+
+- `bgBlack`
+- `bgRed`
+- `bgGreen`
+- `bgYellow`
+- `bgBlue`
+- `bgMagenta`
+- `bgCyan`
+- `bgWhite`
+
+
+## Advanced usage
+
+By default you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module.
+
+- `ansi.modifiers`
+- `ansi.colors`
+- `ansi.bgColors`
+
+
+###### Example
+
+```js
+console.log(ansi.colors.green.open);
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/index.js
new file mode 100644
index 0000000..7834bf9
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/index.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
+
+module.exports = function (str) {
+ if (typeof str !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ return str.replace(matchOperatorsRe, '\\$&');
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/package.json
new file mode 100644
index 0000000..dacd1fe
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "escape-string-regexp",
+ "version": "1.0.4",
+ "description": "Escape RegExp special characters",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/sindresorhus/escape-string-regexp"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "scripts": {
+ "test": "xo && ava"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "escape",
+ "regex",
+ "regexp",
+ "re",
+ "regular",
+ "expression",
+ "string",
+ "str",
+ "special",
+ "characters"
+ ],
+ "devDependencies": {
+ "ava": "*",
+ "xo": "*"
+ },
+ "gitHead": "e9ca6832a9506ca26402cb0e6dc95efcf35b0b97",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/escape-string-regexp/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/escape-string-regexp",
+ "_id": "escape-string-regexp@1.0.4",
+ "_shasum": "b85e679b46f72d03fbbe8a3bf7259d535c21b62f",
+ "_from": "escape-string-regexp@>=1.0.2 <2.0.0",
+ "_npmVersion": "2.14.7",
+ "_nodeVersion": "4.2.1",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "b85e679b46f72d03fbbe8a3bf7259d535c21b62f",
+ "tarball": "http://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.4.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.4.tgz"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/readme.md
new file mode 100644
index 0000000..87ac82d
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/escape-string-regexp/readme.md
@@ -0,0 +1,27 @@
+# escape-string-regexp [](https://travis-ci.org/sindresorhus/escape-string-regexp)
+
+> Escape RegExp special characters
+
+
+## Install
+
+```
+$ npm install --save escape-string-regexp
+```
+
+
+## Usage
+
+```js
+const escapeStringRegexp = require('escape-string-regexp');
+
+const escapedString = escapeStringRegexp('how much $ for a unicorn?');
+//=> 'how much \$ for a unicorn\?'
+
+new RegExp(escapedString);
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/index.js
new file mode 100644
index 0000000..98fae06
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/index.js
@@ -0,0 +1,4 @@
+'use strict';
+var ansiRegex = require('ansi-regex');
+var re = new RegExp(ansiRegex().source); // remove the `g` flag
+module.exports = re.test.bind(re);
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/index.js
new file mode 100644
index 0000000..4906755
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/index.js
@@ -0,0 +1,4 @@
+'use strict';
+module.exports = function () {
+ return /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/package.json
new file mode 100644
index 0000000..7fc0767
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/package.json
@@ -0,0 +1,86 @@
+{
+ "name": "ansi-regex",
+ "version": "2.0.0",
+ "description": "Regular expression for matching ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/ansi-regex.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha test/test.js",
+ "view-supported": "node test/viewCodes.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "57c3f2941a73079fa8b081e02a522e3d29913e2f",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-regex/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-regex",
+ "_id": "ansi-regex@2.0.0",
+ "_shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107",
+ "_from": "ansi-regex@>=2.0.0 <3.0.0",
+ "_npmVersion": "2.11.2",
+ "_nodeVersion": "0.12.5",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107",
+ "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/readme.md
new file mode 100644
index 0000000..1a4894e
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex/readme.md
@@ -0,0 +1,31 @@
+# ansi-regex [](https://travis-ci.org/sindresorhus/ansi-regex)
+
+> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```
+$ npm install --save ansi-regex
+```
+
+
+## Usage
+
+```js
+var ansiRegex = require('ansi-regex');
+
+ansiRegex().test('\u001b[4mcake\u001b[0m');
+//=> true
+
+ansiRegex().test('cake');
+//=> false
+
+'\u001b[4mcake\u001b[0m'.match(ansiRegex());
+//=> ['\u001b[4m', '\u001b[0m']
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/package.json
new file mode 100644
index 0000000..d39a62e
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/package.json
@@ -0,0 +1,85 @@
+{
+ "name": "has-ansi",
+ "version": "2.0.0",
+ "description": "Check if a string has ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/has-ansi.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "node test.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "string",
+ "tty",
+ "escape",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern",
+ "has"
+ ],
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "devDependencies": {
+ "ava": "0.0.4"
+ },
+ "gitHead": "0722275e1bef139fcd09137da6e5550c3cd368b9",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/has-ansi/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/has-ansi",
+ "_id": "has-ansi@2.0.0",
+ "_shasum": "34f5049ce1ecdf2b0649af3ef24e45ed35416d91",
+ "_from": "has-ansi@>=2.0.0 <3.0.0",
+ "_npmVersion": "2.11.2",
+ "_nodeVersion": "0.12.5",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "34f5049ce1ecdf2b0649af3ef24e45ed35416d91",
+ "tarball": "http://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/readme.md
new file mode 100644
index 0000000..02bc7c2
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/has-ansi/readme.md
@@ -0,0 +1,36 @@
+# has-ansi [](https://travis-ci.org/sindresorhus/has-ansi)
+
+> Check if a string has [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```
+$ npm install --save has-ansi
+```
+
+
+## Usage
+
+```js
+var hasAnsi = require('has-ansi');
+
+hasAnsi('\u001b[4mcake\u001b[0m');
+//=> true
+
+hasAnsi('cake');
+//=> false
+```
+
+
+## Related
+
+- [has-ansi-cli](https://github.com/sindresorhus/has-ansi-cli) - CLI for this module
+- [strip-ansi](https://github.com/sindresorhus/strip-ansi) - Strip ANSI escape codes
+- [ansi-regex](https://github.com/sindresorhus/ansi-regex) - Regular expression for matching ANSI escape codes
+- [chalk](https://github.com/sindresorhus/chalk) - Terminal string styling done right
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/index.js
new file mode 100644
index 0000000..099480f
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/index.js
@@ -0,0 +1,6 @@
+'use strict';
+var ansiRegex = require('ansi-regex')();
+
+module.exports = function (str) {
+ return typeof str === 'string' ? str.replace(ansiRegex, '') : str;
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/index.js
new file mode 100644
index 0000000..4906755
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/index.js
@@ -0,0 +1,4 @@
+'use strict';
+module.exports = function () {
+ return /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
+};
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/package.json
new file mode 100644
index 0000000..7fc0767
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/package.json
@@ -0,0 +1,86 @@
+{
+ "name": "ansi-regex",
+ "version": "2.0.0",
+ "description": "Regular expression for matching ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/ansi-regex.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "mocha test/test.js",
+ "view-supported": "node test/viewCodes.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "text",
+ "regex",
+ "regexp",
+ "re",
+ "match",
+ "test",
+ "find",
+ "pattern"
+ ],
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "gitHead": "57c3f2941a73079fa8b081e02a522e3d29913e2f",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/ansi-regex/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/ansi-regex",
+ "_id": "ansi-regex@2.0.0",
+ "_shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107",
+ "_from": "ansi-regex@>=2.0.0 <3.0.0",
+ "_npmVersion": "2.11.2",
+ "_nodeVersion": "0.12.5",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "c5061b6e0ef8a81775e50f5d66151bf6bf371107",
+ "tarball": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/readme.md
new file mode 100644
index 0000000..1a4894e
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex/readme.md
@@ -0,0 +1,31 @@
+# ansi-regex [](https://travis-ci.org/sindresorhus/ansi-regex)
+
+> Regular expression for matching [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```
+$ npm install --save ansi-regex
+```
+
+
+## Usage
+
+```js
+var ansiRegex = require('ansi-regex');
+
+ansiRegex().test('\u001b[4mcake\u001b[0m');
+//=> true
+
+ansiRegex().test('cake');
+//=> false
+
+'\u001b[4mcake\u001b[0m'.match(ansiRegex());
+//=> ['\u001b[4m', '\u001b[0m']
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/package.json
new file mode 100644
index 0000000..2871d03
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/package.json
@@ -0,0 +1,85 @@
+{
+ "name": "strip-ansi",
+ "version": "3.0.0",
+ "description": "Strip ANSI escape codes",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/strip-ansi.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "node test.js"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "strip",
+ "trim",
+ "remove",
+ "ansi",
+ "styles",
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "string",
+ "tty",
+ "escape",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "devDependencies": {
+ "ava": "0.0.4"
+ },
+ "gitHead": "3f05b9810e1438f946e2eb84ee854cc00b972e9e",
+ "bugs": {
+ "url": "https://github.com/sindresorhus/strip-ansi/issues"
+ },
+ "homepage": "https://github.com/sindresorhus/strip-ansi",
+ "_id": "strip-ansi@3.0.0",
+ "_shasum": "7510b665567ca914ccb5d7e072763ac968be3724",
+ "_from": "strip-ansi@>=3.0.0 <4.0.0",
+ "_npmVersion": "2.11.2",
+ "_nodeVersion": "0.12.5",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "7510b665567ca914ccb5d7e072763ac968be3724",
+ "tarball": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/readme.md
new file mode 100644
index 0000000..7609151
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/strip-ansi/readme.md
@@ -0,0 +1,33 @@
+# strip-ansi [](https://travis-ci.org/sindresorhus/strip-ansi)
+
+> Strip [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code)
+
+
+## Install
+
+```
+$ npm install --save strip-ansi
+```
+
+
+## Usage
+
+```js
+var stripAnsi = require('strip-ansi');
+
+stripAnsi('\u001b[4mcake\u001b[0m');
+//=> 'cake'
+```
+
+
+## Related
+
+- [strip-ansi-cli](https://github.com/sindresorhus/strip-ansi-cli) - CLI for this module
+- [has-ansi](https://github.com/sindresorhus/has-ansi) - Check if a string has ANSI escape codes
+- [ansi-regex](https://github.com/sindresorhus/ansi-regex) - Regular expression for matching ANSI escape codes
+- [chalk](https://github.com/sindresorhus/chalk) - Terminal string styling done right
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/index.js b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/index.js
new file mode 100644
index 0000000..4346e27
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/index.js
@@ -0,0 +1,50 @@
+'use strict';
+var argv = process.argv;
+
+var terminator = argv.indexOf('--');
+var hasFlag = function (flag) {
+ flag = '--' + flag;
+ var pos = argv.indexOf(flag);
+ return pos !== -1 && (terminator !== -1 ? pos < terminator : true);
+};
+
+module.exports = (function () {
+ if ('FORCE_COLOR' in process.env) {
+ return true;
+ }
+
+ if (hasFlag('no-color') ||
+ hasFlag('no-colors') ||
+ hasFlag('color=false')) {
+ return false;
+ }
+
+ if (hasFlag('color') ||
+ hasFlag('colors') ||
+ hasFlag('color=true') ||
+ hasFlag('color=always')) {
+ return true;
+ }
+
+ if (process.stdout && !process.stdout.isTTY) {
+ return false;
+ }
+
+ if (process.platform === 'win32') {
+ return true;
+ }
+
+ if ('COLORTERM' in process.env) {
+ return true;
+ }
+
+ if (process.env.TERM === 'dumb') {
+ return false;
+ }
+
+ if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) {
+ return true;
+ }
+
+ return false;
+})();
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/license b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/package.json
new file mode 100644
index 0000000..38a1ecb
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/package.json
@@ -0,0 +1,79 @@
+{
+ "name": "supports-color",
+ "version": "2.0.0",
+ "description": "Detect whether a terminal supports color",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/chalk/supports-color.git"
+ },
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.8.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "ansi",
+ "styles",
+ "tty",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "command-line",
+ "support",
+ "supports",
+ "capability",
+ "detect"
+ ],
+ "devDependencies": {
+ "mocha": "*",
+ "require-uncached": "^1.0.2"
+ },
+ "gitHead": "8400d98ade32b2adffd50902c06d9e725a5c6588",
+ "bugs": {
+ "url": "https://github.com/chalk/supports-color/issues"
+ },
+ "homepage": "https://github.com/chalk/supports-color",
+ "_id": "supports-color@2.0.0",
+ "_shasum": "535d045ce6b6363fa40117084629995e9df324c7",
+ "_from": "supports-color@>=2.0.0 <3.0.0",
+ "_npmVersion": "2.11.2",
+ "_nodeVersion": "0.12.5",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "535d045ce6b6363fa40117084629995e9df324c7",
+ "tarball": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/readme.md
new file mode 100644
index 0000000..b4761f1
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/node_modules/supports-color/readme.md
@@ -0,0 +1,36 @@
+# supports-color [](https://travis-ci.org/chalk/supports-color)
+
+> Detect whether a terminal supports color
+
+
+## Install
+
+```
+$ npm install --save supports-color
+```
+
+
+## Usage
+
+```js
+var supportsColor = require('supports-color');
+
+if (supportsColor) {
+ console.log('Terminal supports color');
+}
+```
+
+It obeys the `--color` and `--no-color` CLI flags.
+
+For situations where using `--color` is not possible, add an environment variable `FORCE_COLOR` with any value to force color. Trumps `--no-color`.
+
+
+## Related
+
+- [supports-color-cli](https://github.com/chalk/supports-color-cli) - CLI for this module
+- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/package.json b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/package.json
new file mode 100644
index 0000000..46844bd
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/package.json
@@ -0,0 +1,102 @@
+{
+ "name": "chalk",
+ "version": "1.1.1",
+ "description": "Terminal string styling done right. Much color.",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/chalk/chalk.git"
+ },
+ "maintainers": [
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jbnicolai",
+ "email": "jappelman@xebia.com"
+ },
+ {
+ "name": "unicorn",
+ "email": "sindresorhus+unicorn@gmail.com"
+ }
+ ],
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "xo && mocha",
+ "bench": "matcha benchmark.js",
+ "coverage": "nyc npm test && nyc report",
+ "coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls"
+ },
+ "files": [
+ "index.js"
+ ],
+ "keywords": [
+ "color",
+ "colour",
+ "colors",
+ "terminal",
+ "console",
+ "cli",
+ "string",
+ "str",
+ "ansi",
+ "style",
+ "styles",
+ "tty",
+ "formatting",
+ "rgb",
+ "256",
+ "shell",
+ "xterm",
+ "log",
+ "logging",
+ "command-line",
+ "text"
+ ],
+ "dependencies": {
+ "ansi-styles": "^2.1.0",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "devDependencies": {
+ "coveralls": "^2.11.2",
+ "matcha": "^0.6.0",
+ "mocha": "*",
+ "nyc": "^3.0.0",
+ "require-uncached": "^1.0.2",
+ "resolve-from": "^1.0.0",
+ "semver": "^4.3.3",
+ "xo": "*"
+ },
+ "xo": {
+ "envs": [
+ "node",
+ "mocha"
+ ]
+ },
+ "gitHead": "8b554e254e89c85c1fd04dcc444beeb15824e1a5",
+ "bugs": {
+ "url": "https://github.com/chalk/chalk/issues"
+ },
+ "homepage": "https://github.com/chalk/chalk#readme",
+ "_id": "chalk@1.1.1",
+ "_shasum": "509afb67066e7499f7eb3535c77445772ae2d019",
+ "_from": "chalk@1.1.1",
+ "_npmVersion": "2.13.5",
+ "_nodeVersion": "0.12.7",
+ "_npmUser": {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ "dist": {
+ "shasum": "509afb67066e7499f7eb3535c77445772ae2d019",
+ "tarball": "http://registry.npmjs.org/chalk/-/chalk-1.1.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.1.tgz"
+}
diff --git a/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/readme.md b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/readme.md
new file mode 100644
index 0000000..5cf111e
--- /dev/null
+++ b/grocery-api/node_modules/heroku-cli-util/node_modules/chalk/readme.md
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
+
+
+> Terminal string styling done right
+
+[](https://travis-ci.org/chalk/chalk)
+[](https://coveralls.io/r/chalk/chalk?branch=master)
+[](https://www.youtube.com/watch?v=9auOCbH5Ns4)
+
+
+[colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68). Although there are other ones, they either do too much or not enough.
+
+**Chalk is a clean and focused alternative.**
+
+
+
+
+## Why
+
+- Highly performant
+- Doesn't extend `String.prototype`
+- Expressive API
+- Ability to nest styles
+- Clean and focused
+- Auto-detects color support
+- Actively maintained
+- [Used by ~4500 modules](https://www.npmjs.com/browse/depended/chalk) as of July 15, 2015
+
+
+## Install
+
+```
+$ npm install --save chalk
+```
+
+
+## Usage
+
+Chalk comes with an easy to use composable API where you just chain and nest the styles you want.
+
+```js
+var chalk = require('chalk');
+
+// style a string
+chalk.blue('Hello world!');
+
+// combine styled and normal strings
+chalk.blue('Hello') + 'World' + chalk.red('!');
+
+// compose multiple styles using the chainable API
+chalk.blue.bgRed.bold('Hello world!');
+
+// pass in multiple arguments
+chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz');
+
+// nest styles
+chalk.red('Hello', chalk.underline.bgBlue('world') + '!');
+
+// nest styles of the same type even (color, underline, background)
+chalk.green(
+ 'I am a green line ' +
+ chalk.blue.underline.bold('with a blue substring') +
+ ' that becomes green again!'
+);
+```
+
+Easily define your own themes.
+
+```js
+var chalk = require('chalk');
+var error = chalk.bold.red;
+console.log(error('Error!'));
+```
+
+Take advantage of console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data).
+
+```js
+var name = 'Sindre';
+console.log(chalk.green('Hello %s'), name);
+//=> Hello Sindre
+```
+
+
+## API
+
+### chalk.`
+
+
+
+
+
+
+
The page you were looking for doesn't exist.
+
You may have mistyped the address or the page may have moved.
+
+
If you are the application owner check the logs for more information.
+
+
+