Full Code of strongloop/loopback for AI

master 13371fd2a138 cached
242 files
1.2 MB
334.1k tokens
299 symbols
1 requests
Download .txt
Showing preview only (1,335K chars total). Download the full file or copy to clipboard to get everything.
Repository: strongloop/loopback
Branch: master
Commit: 13371fd2a138
Files: 242
Total size: 1.2 MB

Directory structure:
gitextract_fjzzp53t/

├── .eslintignore
├── .eslintrc
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── Bug_report.md
│   │   ├── Feature_request.md
│   │   ├── Question.md
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── stale.yml
├── .gitignore
├── .npmrc
├── .nycrc
├── .travis.yml
├── CHANGES.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── common/
│   └── models/
│       ├── README.md
│       ├── access-token.js
│       ├── access-token.json
│       ├── acl.js
│       ├── acl.json
│       ├── application.js
│       ├── application.json
│       ├── change.js
│       ├── change.json
│       ├── checkpoint.js
│       ├── checkpoint.json
│       ├── email.js
│       ├── email.json
│       ├── key-value-model.js
│       ├── key-value-model.json
│       ├── role-mapping.js
│       ├── role-mapping.json
│       ├── role.js
│       ├── role.json
│       ├── scope.js
│       ├── scope.json
│       ├── user.js
│       └── user.json
├── docs/
│   └── api-explorer-details.md
├── docs.json
├── example/
│   ├── client-server/
│   │   ├── client.js
│   │   ├── models.js
│   │   └── server.js
│   ├── colors/
│   │   └── app.js
│   ├── mobile-models/
│   │   └── app.js
│   ├── replication/
│   │   └── app.js
│   └── simple-data-source/
│       └── app.js
├── index.js
├── intl/
│   ├── cs/
│   │   └── messages.json
│   ├── de/
│   │   └── messages.json
│   ├── en/
│   │   └── messages.json
│   ├── es/
│   │   └── messages.json
│   ├── fr/
│   │   └── messages.json
│   ├── it/
│   │   └── messages.json
│   ├── ja/
│   │   └── messages.json
│   ├── ko/
│   │   └── messages.json
│   ├── nl/
│   │   └── messages.json
│   ├── pl/
│   │   └── messages.json
│   ├── pt/
│   │   └── messages.json
│   ├── ru/
│   │   └── messages.json
│   ├── tr/
│   │   └── messages.json
│   ├── zh-Hans/
│   │   └── messages.json
│   └── zh-Hant/
│       └── messages.json
├── lib/
│   ├── access-context.js
│   ├── application.js
│   ├── browser-express.js
│   ├── builtin-models.js
│   ├── configure-shared-methods.js
│   ├── connectors/
│   │   ├── base-connector.js
│   │   ├── mail.js
│   │   └── memory.js
│   ├── current-context.js
│   ├── globalize.js
│   ├── loopback.js
│   ├── model.js
│   ├── persisted-model.js
│   ├── registry.js
│   ├── runtime.js
│   ├── server-app.js
│   └── utils.js
├── package.json
├── server/
│   └── middleware/
│       ├── context.js
│       ├── error-handler.js
│       ├── favicon.js
│       ├── rest.js
│       ├── static.js
│       ├── status.js
│       ├── token.js
│       └── url-not-found.js
├── templates/
│   ├── reset-form.ejs
│   └── verify.ejs
└── test/
    ├── access-control.integration.js
    ├── access-token.test.js
    ├── acl.test.js
    ├── app.test.js
    ├── authorization-scopes.test.js
    ├── change-stream.test.js
    ├── change.test.js
    ├── checkpoint.test.js
    ├── context-options.test.js
    ├── data-source.test.js
    ├── e2e/
    │   ├── remote-connector.e2e.js
    │   └── replication.e2e.js
    ├── email.test.js
    ├── error-handler.test.js
    ├── fixtures/
    │   ├── access-control/
    │   │   ├── common/
    │   │   │   └── models/
    │   │   │       ├── access-token.json
    │   │   │       ├── account.json
    │   │   │       ├── accountWithReplaceOnPUTfalse.json
    │   │   │       ├── alert.json
    │   │   │       ├── bank.json
    │   │   │       ├── email.json
    │   │   │       ├── transaction.json
    │   │   │       └── user.json
    │   │   └── server/
    │   │       ├── config.json
    │   │       ├── datasources.json
    │   │       ├── model-config.json
    │   │       └── server.js
    │   ├── e2e/
    │   │   └── server/
    │   │       ├── models.js
    │   │       └── server.js
    │   ├── shared-methods/
    │   │   ├── both-configs-set/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-default-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-default-true/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-defined-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-defined-true/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── model-config-default-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── model-config-default-true/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── model-config-defined-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   └── model-config-defined-true/
    │   │       ├── common/
    │   │       │   └── models/
    │   │       │       ├── todo.js
    │   │       │       └── todo.json
    │   │       └── server/
    │   │           ├── config.json
    │   │           ├── datasources.json
    │   │           ├── model-config.json
    │   │           └── server.js
    │   ├── simple-app/
    │   │   ├── boot/
    │   │   │   ├── bad.txt
    │   │   │   └── foo.js
    │   │   ├── common/
    │   │   │   └── models/
    │   │   │       ├── bar.js
    │   │   │       ├── bar.json
    │   │   │       └── foo.json
    │   │   └── server/
    │   │       ├── config.json
    │   │       ├── datasources.json
    │   │       └── model-config.json
    │   ├── simple-integration-app/
    │   │   ├── common/
    │   │   │   └── models/
    │   │   │       ├── access-token.json
    │   │   │       ├── appointment.json
    │   │   │       ├── customer-forceid.json
    │   │   │       ├── customer.json
    │   │   │       ├── email.json
    │   │   │       ├── patient.json
    │   │   │       ├── physician.json
    │   │   │       ├── profile.json
    │   │   │       ├── store-replacing.json
    │   │   │       ├── store-updating.json
    │   │   │       ├── store.json
    │   │   │       ├── user.json
    │   │   │       └── widget.json
    │   │   └── server/
    │   │       ├── config.json
    │   │       ├── datasources.json
    │   │       ├── model-config.json
    │   │       └── server.js
    │   └── user-integration-app/
    │       ├── common/
    │       │   └── models/
    │       │       ├── blog.json
    │       │       ├── my-user.json
    │       │       └── post.json
    │       └── server/
    │           ├── config.json
    │           ├── datasources.json
    │           ├── model-config.json
    │           └── server.js
    ├── geo-point.test.js
    ├── helpers/
    │   ├── error-loggers.js
    │   ├── expect.js
    │   ├── loopback-testing-helper.js
    │   ├── use-english.js
    │   └── wait-for-event.js
    ├── hidden-properties.test.js
    ├── integration.test.js
    ├── karma.conf.js
    ├── key-value-model.test.js
    ├── loopback.test.js
    ├── memory.test.js
    ├── mocha.opts
    ├── model.application.test.js
    ├── model.test.js
    ├── multiple-user-principal-accessing-another-user-model.js
    ├── multiple-user-principal-types.test.js
    ├── registries.test.js
    ├── relations.integration.js
    ├── remote-connector.test.js
    ├── remoting-coercion.test.js
    ├── remoting.integration.js
    ├── replication.rest.test.js
    ├── replication.test.js
    ├── rest.middleware.test.js
    ├── role-mapping.test.js
    ├── role.test.js
    ├── user-password.test.js
    ├── user.integration.js
    ├── user.test.js
    ├── util/
    │   ├── describe.js
    │   ├── it.js
    │   └── model-tests.js
    └── utils.test.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintignore
================================================
dist
coverage


================================================
FILE: .eslintrc
================================================
{
  "extends": "loopback",
  "rules": {
    "max-len": ["error", 100, 4, {
      "ignoreComments": true,
      "ignoreUrls": true,
      "ignorePattern": "^\\s*var\\s.+=\\s*(require\\s*\\()|(/)"
    }]
  }
}


================================================
FILE: .github/ISSUE_TEMPLATE/Bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
labels: bug

---

<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨

Are you using LoopBack version 4? Please report the bug here:
https://github.com/strongloop/loopback-next/issues/new

HELP US HELP YOU, PLEASE
- Do a quick search to avoid duplicate issues
- Provide as much information as possible (reproduction sandbox, use case for features, etc.)
- Consider using a more suitable venue for questions such as Stack Overflow, Gitter, etc.

Please fill in the *entire* template below.

-->

## Steps to reproduce

<!-- Describe how to reproduce the issue -->

## Current Behavior

<!-- Describe the observed result -->

## Expected Behavior

<!-- Describe what did you expect instead, what is the desired outcome? -->

## Link to reproduction sandbox

<!--
See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-3x-bugs
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
-->

## Additional information

<!--
Copy+paste the output of these two commands:
  node -e 'console.log(process.platform, process.arch, process.versions.node)'
  npm ls --prod --depth 0 | grep loopback
-->

## Related Issues

<!-- Did you find other bugs that looked similar? -->

_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_


================================================
FILE: .github/ISSUE_TEMPLATE/Feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
labels: feature

---

<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨

LoopBack version 3 is in LTS mode, we are not accepting new features.

We are actively developing version 4, you can find the new GitHub
repository here: https://github.com/strongloop/loopback-next

-->

## Suggestion

<!-- A summary of what you'd like to see added or changed -->

## Use Cases

<!--
What do you want to use this for?
What shortcomings exist with current approaches?
-->

## Examples

<!-- Show how this would be used and what the behavior would be -->

## Acceptance criteria

TBD - will be filled by the team.


================================================
FILE: .github/ISSUE_TEMPLATE/Question.md
================================================
---
name: Question
about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help.
labels: question

---

<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨

THE ISSUE TRACKER IS NOT FOR QUESTIONS.

DO NOT CREATE A NEW ISSUE TO ASK A QUESTION.

Please use one of the following resources for help:

**Questions**

- https://stackoverflow.com/tags/loopbackjs
- https://groups.google.com/forum/#!forum/loopbackjs
- https://gitter.im/strongloop/loopback

**Immediate support**

- https://strongloop.com/api-connect-faqs/
- https://strongloop.com/node-js/subscription-plans/

-->


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Report a security vulnerability
    url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues
    about: >
      LoopBack 3 has reached End-of-Life. No new security fixes will be provided
      or accepted.
      
      Do not report security vulnerabilities using GitHub issues. Please send an
      email to `reachsl@us.ibm.com` instead.
  - name: Get help on StackOverflow
    url: https://stackoverflow.com/tags/loopbackjs
    about: Please ask and answer questions on StackOverflow.
  - name: Join our mailing list
    url: https://groups.google.com/forum/#!forum/loopbackjs
    about: You can also post your question to our mailing list.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Please provide a high-level description of the changes made by your pull request.

Include references to all related GitHub issues and other pull requests, for example:

Fixes #123
Implements #254
See also #23
-->

## Checklist

👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/loopback) 👈

- [ ] `npm test` passes on your machine
- [ ] New tests added or existing tests modified to cover all changes
- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html)
- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html)


================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
  - pinned
  - security
  - critical
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
  This issue has been automatically marked as stale because it has not had
  recent activity. It will be closed if no further activity occurs. Thank you
  for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
  This issue has been closed due to continued inactivity. Thank you for your understanding.
  If you believe this to be in error, please contact one of the code owners,
  listed in the [`CODEOWNERS`](https://github.com/strongloop/loopback/blob/master/CODEOWNERS) file at the top-level of this repository.


================================================
FILE: .gitignore
================================================
.idea
.project
.DS_Store
.vscode/
*.sublime*
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.swp
*.swo
node_modules
dist
*xunit.xml
.nyc_output/


================================================
FILE: .npmrc
================================================
package-lock=false


================================================
FILE: .nycrc
================================================
{
  "exclude":  [
    "Gruntfile.js",
    "test/**/*.js"
  ],
  "cache": true
}


================================================
FILE: .travis.yml
================================================
sudo: false
language: node_js
node_js:
  - "8"
  - "10"
  - "12"
  - "14"

addons:
  chrome: stable

after_success: npm run coverage

before_install:
  - npm config set registry http://ci.strongloop.com:4873/


================================================
FILE: CHANGES.md
================================================
2020-11-25, Version 3.28.0
==========================

 * upgrade nodemailer to greater than 6.4.16 (jannyHou)

 * chore: sync LoopBack 4 with Node.js v14 EOL (Rifa Achrinza)

 * chore: add Node.js 14 to travis (Diana Lau)

 * Remove "major" and "p1" from stalebot (Miroslav Bajtoš)


2020-03-06, Version 3.27.0
==========================

 * Update LTS status in README (Miroslav Bajtoš)

 * chore: update copyright year (Diana Lau)

 * feat: change hasone relation error message (Sujesh T)

 * chore: disable security issue reporting (Nora)

 * chore: fix eslint violations (Nora)

 * fixup! manual fixes (Miroslav Bajtoš)

 * fixup! eslint --fix . (Miroslav Bajtoš)

 * chore: update eslint & eslint-config to latest (Miroslav Bajtoš)

 * chore: update dev-dependencies (Miroslav Bajtoš)

 * chore: update chai to v4, dirty-chai to v2 (Miroslav Bajtoš)

 * Updated "ismail" package to v3.2 (Stanislav Sarbinski)

 * Introduce issue templates for bugs, features, etc. (Miroslav Bajtoš)

 * Improve PULL_REQUEST_TEMPLATE (Miroslav Bajtoš)

 * test: use Chromium (not Chrome) when available (Miroslav Bajtoš)

 * test: disable Chrome sandboxing when inside Docker (Miroslav Bajtoš)

 * test: switch from PhantomJS to HeadlessChrome (Miroslav Bajtoš)


2019-05-31, Version 3.26.0
==========================

 * fix: disallow queries in username and email fields (Hage Yaapa)

 * Ignore failing downstream dependencies (Miroslav Bajtoš)

 * Upgrade nyc to version 14 (Miroslav Bajtoš)

 * Update Karma dependencies to latest versions (Miroslav Bajtoš)

 * Drop Node.js 6.x from the supported versions (Miroslav Bajtoš)

 * Fix Model.exists() to work with remote connector (Maxim Sharai)

 * chore: update copyrights years (Agnes Lin)

 * Update LTS status (Diana Lau)

 * Enable Node.js 12.x on Travis CI (Miroslav Bajtoš)

 * chore: update copyright year (Diana Lau)

 * chore: update LB3 EOL date (Diana Lau)


2019-03-15, Version 3.25.1
==========================

 * Back-ticks added to highlight example JSON (Quentin Presley)

 * Add same change to description for findOne (Quentin Presley)

 * Update the description for persisted-models (Quentin Presley)

 * handle $2b$ in hashed password check (Sylvain Dumont)


2019-02-05, Version 3.25.0
==========================

 * Support middleware injected by AppDynamics. (Mike Li)


2019-01-11, Version 3.24.2
==========================

 * Fix crash when modifying an unknown user (Matheus Horstmann)


2019-01-08, Version 3.24.1
==========================

 * Update underscore.string to 3.3.5 (Francois)

 * Fix: treat empty access token string as undefined (andrey-abramow)


2018-11-15, Version 3.24.0
==========================

 * Set juggler options for remote calls (Raymond Feng)

 * Speed up ACL tests by reducing saltWorkFactor (Miroslav Bajtoš)


2018-10-25, Version 3.23.2
==========================

 * Fix ACL check to support model wildcard (Moshe Malka)


2018-10-18, Version 3.23.1
==========================

 * README: highlight Active LTS at the top (Miroslav Bajtoš)


2018-10-09, Version 3.23.0
==========================

 * Clear handler cache when a method is added/removed (Mohammed Essehemy)

 * Add `options.preserveAccessTokens` (lchaglla)

 * Update LB3 to be active LTS (Diana Lau)

 * Fix ACL tests to wait until all assertions finish (Moshe Malka)

 * chore: update to latest linting rules (virkt25)


2018-09-12, Version 3.22.3
==========================

 * chore: use grunt to install optional  phantomjs (virkt25)

 * [WebFM] fr translation (candytangnb)


2018-08-29, Version 3.22.2
==========================

 * [WebFM] tr translation (candytangnb)

 * [WebFM] de translation (candytangnb)

 * [WebFM] cs/es/fr/it/nl/pl/pt_BR/ru translation (candytangnb)


2018-08-22, Version 3.22.1
==========================

 * [WebFM] ja/ko/zh_CN/zh_TW translation (candytangnb)

 * remove unnecessary format call (Diana Lau)

 * Make desc when export-api-def translatable (Diana Lau)


2018-08-08, Version 3.22.0
==========================

 * fix: accessToken create default acl (virkt25)

 * add: ppc64 and s390x to not run UI tests (Thomas Leah)

 * chore: update deps + fix linting + .npmrc (virkt25)

 * Update Loopback 2.x EOL dates (Chris Bailey)

 * Fix formatting (Chris Bailey)

 * Update support badge and move LTS section (Chris Bailey)

 * Add badges and information for LTS and support (Chris Bailey)


2018-07-09, Version 3.21.0
==========================

 * Make verifyUserRelations() more robust (mcitdev)

 * Fix crash in verifyUserRelations (ryanxwelch)

 * Fix crash in User model's "before delete" hook (mcitdev)

 * [WebFM] cs/pl/ru translation (candytangnb)

 * Update strong-error-handler (shimks)


2018-06-12, Version 3.20.0
==========================

 * Update strong-globalize to 4.x (Miroslav Bajtoš)

 * Update nodemailer to v4.x (Dimitris)

 * Drop support for Node.js 4.x (Miroslav Bajtoš)


2018-06-04, Version 3.19.3
==========================

 * Provide link to CODEOWNERS (Aditya Agarwal)

 * fix bug in User.verify when confirm is disabled (wolrajhti)

 * Enable Node.js 10.x on Travis CI (Miroslav Bajtoš)


2018-05-29, Version 3.19.2
==========================

 * Add check for undefined user email in setter (Kevin Scroggins)


2018-05-21, Version 3.19.1
==========================

 * Fix isOwner() bug in multiple-principal setup (Miroslav Bajtoš)


2018-04-17, Version 3.19.0
==========================

 * feat: remove all references to a Model (Miroslav Bajtoš)


2018-03-22, Version 3.18.3
==========================

 * Remove forgotten debugger statement (Miroslav Bajtoš)

 * Fix role check in apps with multiple user models (Miroslav Bajtoš)

 * Fix formatting issues reported by recent eslint (Miroslav Bajtoš)

 * CODEOWNERS: add nitro404 (Miroslav Bajtoš)

 * test: add missing "return" in a promise-style test (Miroslav Bajtoš)


2018-02-08, Version 3.18.2
==========================

 * model: fix infinite loop on nestRemoting (Kevin Delisle)

 * Use statusCode prop for user errors (Zak Barbuto)


2018-01-31, Version 3.18.1
==========================

 * update: juggler to version including security fix. (Taranveer Virk)


2018-01-29, Version 3.18.0
==========================

 * fix: preserve datasource name (Kevin Scroggins)

 * Update Copyright Years (Justin Ross)

 * Support options.filter in createChangeStream (Edward Choh)

 * fixup! add top-level dep on eslint-plugin-mocha (Miroslav Bajtoš)

 * Update eslint and eslint-config to latest (Miroslav Bajtoš)


2017-12-12, Version 3.17.1
==========================

 * Update nestRemoting to pass optionsFromContext (bmatson)

 * fix(test): rem exclusive test (Samuel Reed)

 * fix(test): working test with 0 userId (Samuel Reed)

 * fix(AccessContext): Tighten userid/appid checks (Samuel Reed)

 * fix(id): replace with != null (Samuel Reed)


2017-11-29, Version 3.17.0
==========================

 * Added missing DateString type in loopback index (CSLTech)

 * chore:update license (Diana Lau)


2017-10-30, Version 3.16.2
==========================

 * Fix "POST /change-password" for multi-user setup (Miroslav Bajtoš)


2017-10-27, Version 3.16.1
==========================

 * Fix createOnlyInstance for related methods (Raymond Feng)


2017-10-24, Version 3.16.0
==========================

 * Fix "POST /reset-password" for multi-user setup (Miroslav Bajtoš)

 * test: extract helpers for logging HTTP errors (Miroslav Bajtoš)

 * CODEOWNERS: move @lehni to Alumni section (Miroslav Bajtoš)


2017-10-13, Version 3.15.0
==========================

 * update strong-globalize to 3.1.0 (shimks)

 * Fix handling of user verification options (Miroslav Bajtoš)

 * Handle missing getUpdateOnlyProperties fn (Jürg Lehni)

 * test: fix too strict test assertion (Miroslav Bajtoš)

 * Fix typo (Siegfried Ehret)


2017-09-28, Version 3.14.0
==========================

 * Allow declarative nestRemoting for relations (Raymond Feng)


2017-09-27, Version 3.13.0
==========================

 * Fix OWNER role to handle multiple relations (pierreclr)

 * Fix acl.resolvePermission for wildcard req (Farid Neshat)

 * CODEOWNERS: add zbarbuto (Miroslav Bajtoš)


2017-09-25, Version 3.12.0
==========================

 * Fix relation race condition in model glob (Zak Barbuto)

 * CODEOWNERS: add lehni (Miroslav Bajtoš)


2017-08-23, Version 3.11.1
==========================

 * Handle missing getUpdateOnlyProperties fn (Kevin Delisle)


2017-08-22, Version 3.11.0
==========================

 * Support createOnlyInstance in model (#3548) (Rashmi Hunt)

 * Add stalebot configuration (Kevin Delisle)

 * Catch errors on invalidate update (loay)

 * Update Issue and PR Templates (#3568) (Sakib Hasan)


2017-08-16, Version 3.10.1
==========================

 * fix(validatePassword): reword error message (Samuel Reed)

 * Do not add isStatic properties to method settings (Jürg Lehni)


2017-08-14, Version 3.10.0
==========================

 * Allow glob-style patterns for remote options (Zak Barbuto)

 * Fix case of values per doc issue (crandmck)

 * Update translated strings Q3 2017 (Allen Boone)

 * Revert "Validate on updateAll" (Sakib Hasan)

 * Add tests of HTTP normalization on app level (Jürg Lehni)

 * travis: drop Node.js 7.x, add 8.x (Miroslav Bajtoš)

 * Validate on updateAll (ssh24)

 * Update juggler version (loay)

 * update messages.json (Diana Lau)

 * small fix for the title (Michael Alaev)

 * Changed http to https (Michael Alaev)

 * Update Travis registry (loay)

 * Add unit test for empty password (loay)

 * Add CODEOWNER file (Diana Lau)


2017-07-12, Version 3.9.0
=========================

 * Remove observers from Model on end of the stream (Alexei Smirnov)

 * Fix Model#settings.acls doc type signature (Farid Nouri Neshat)

 * Use `localhost` instead of `::` for local (Daijiro Wachi)

 * Fix API doc for Model class property type (Candy)

 * Update package.json (sqlwwx)

 * Support remoting adapters with no ctx.req object (Piero Maltese)

 * update strong-error-handler (sqlwwx)


2017-05-02, Version 3.8.0
=========================

 * Refactor access token to make it extensible (Raymond Feng)


2017-04-27, Version 3.7.0
=========================

 * Remote method /user/:id/verify (ebarault)

 * Implement more secure password flow (Miroslav Bajtoš)

 * Add User.setPassword(id, new, cb) (Miroslav Bajtoš)

 * Fix method setup in authorization-scopes.test (Miroslav Bajtoš)

 * Add missing tests for reset password flow (Miroslav Bajtoš)

 * forwarding context options in user.verify (ebarault)

 * update deprecated dependencies (Diana Lau)

 * Add support for scoped access tokens (Miroslav Bajtoš)

 * Fix user-literal rewrite for anonymous requests (Aaron Buchanan)


2017-03-31, Version 3.6.0
=========================

 * Add new event "remoteMethodAdded" (Flavien DAVID)

 * Forward options in prepareForTokenInvalidation (Miroslav Bajtoš)

 * Check max password length in User.changePassword (Miroslav Bajtoš)

 * Add User.changePassword(id, old, new, cb) (Miroslav Bajtoš)

 * Propagate authorized roles in remoting context (ebarault)

 * Run the latest Node.js 7 version on Travis again (Miroslav Bajtoš)

 * Lock down Travis CI Node 7 version to 7.7.1 (Miroslav Bajtoš)

 * README: add a link to our announcements list (Miroslav Bajtoš)

 * Allow custom properties of Change Model (agriwebb build)

 * Fix User.verify to convert uid to string (phairow)

 * Pass options.verificationToken to templateFn (Hiran del Castillo)

 * fix custom token model in token middleware (ebarault)

 * Update runtime dependencies (Miroslav Bajtoš)

 * Verify User and AccessToken relations at startup (Miroslav Bajtoš)

 * Deep-clone model settings in lib/builtin-models (Miroslav Bajtoš)

 * Use local registry in test/replication.rest.test (Miroslav Bajtoš)

 * Fix test/access-token.test to use local registry (Miroslav Bajtoš)

 * Fix context passing in OWNER role resolver (Benjamin Schuster-Boeckler)


2017-02-24, Version 3.4.0
=========================

 * Fix access-token invalidation for missing relation (Miroslav Bajtoš)

 * Configure Travis CI to cache phantomjs binaries (Miroslav Bajtoš)

 * Optimise replication (kobaska)

 * Improve "filter" arg description (Raymond Camden)


2017-02-17, Version 3.3.0
=========================

 * Fix Role.isOwner() for multiple user models (ebarault)

 * Update ISSUE_TEMPLATE.md (Simon Ho)

 * Upgrade supertest to 3.x (Miroslav Bajtoš)

 * Fix creation of verification links (Miroslav Bajtoš)

 * Enable multiple user models (Eric)

 * Babelify juggler for Karma tests (Miroslav Bajtoš)

 * Fix Karma config to babelify node_modules too (Miroslav Bajtoš)

 * Add promise support to built-in model RoleMapping (ebarault)

 * Add promise support to built-in model ACL (ebarault)

 * Add nyc coverage, report data to coveralls.io (Miroslav Bajtoš)

 * Upgrade eslint config, fix linter errors (Miroslav Bajtoš)

 * Add missing type to Role properties definition (David Hernandez)

 * Preserve sessions on User.save() making no changes (Miroslav Bajtoš)

 * Fix logout to handle no or missing accessToken (Ritchie Martori)

 * Promise-ify built-in Role model (Miroslav Bajtoš)

 * Remove .jscsrc that's no longer used (Miroslav Bajtoš)

 * Enable ES6/ES2015 goodness (Miroslav Bajtoš)

 * Remove test/support.js from karma config (Miroslav Bajtoš)

 * Use English when running Mocha tests (Miroslav Bajtoš)

 * Update ISSUE_TEMPLATE (Simon Ho)

 * Updating README - add cli and remove arc (Joe Sepi)

 * Fix User methods to use correct Primary Key (Aris Kemper)

 * Fix User.resetPassword to call createAccessToken() (João Ribeiro)

 * Role model: resolves related models by name (Benjamin Kroeger)


2017-01-16, Version 3.2.1
=========================

 * Preserve current session when invalidating tokens (Miroslav Bajtoš)

 * Clean up access-token-invalidation tests (Miroslav Bajtoš)

 * Update docs.json (Rand McKinney)

 * Simplify issue template (#3083) (Simon Ho)

 * Warn about injectOptionsFromRemoteContext (Miroslav Bajtoš)


2017-01-09, Version 3.2.0
=========================

 * Upgrade eslint-config to 7.x (Miroslav Bajtoš)

 * Allow password reset request for users in realms (Bram Borggreve)

 * Fix construction of sharedCtor remoting metadata (Miroslav Bajtoš)

 * Add option disabling periodic change rectification (kobaska)

 * Fix annotation for persistedModel.count (lschricke)

 * Applied as reviewed by @flowersinthesand (박대선)

 * Fix false emailVerified on user model update (박대선)

 * Contextify DAO and relation methods (Miroslav Bajtoš)

 * Implement new http arg mapping optionsFromRequest (Miroslav Bajtoš)

 * Emit resetPasswordRequest event with options (Sergey Reus)


2016-12-21, Version 3.1.1
=========================

 * Update package.json for LB3 release (Simon Ho)

 * Invalidate AccessTokens on password change (Miroslav Bajtoš)

 * Fix registration of operation hooks in User model (Miroslav Bajtoš)

 * Remove "options.template" from Email payload (Miroslav Bajtoš)

 * Upgrade eslint config and grunt-eslint to latest (Miroslav Bajtoš)

 * Update paid support URL (siddhipai)

 * Update paid support URL (Siddhi Pai)

 * Remove duplicate warning in issue template (Siddhi Pai)


2016-12-05, Version 3.1.0
=========================

 * Fix use-strict issue with connectors after merge (Loay)

 * Fix connector naming in strict mode (ebarault)

 * Add "returnOnlyRoleNames" option to Role.getRoles (Eric)

 * Update translation files (Candy)

 * Fix broken document for `upsertWithWhere` (Amir Jafarian)

 * Fix js doc for deleteAll event (Candy)

 * add allowArray to relations' create remoteMethod (David Cheung)

 * Remove workaround for default value (Loay)

 * Fix remote method example (Amir Jafarian)

 * Remove `example/context` (Amir Jafarian)

 * Turn on "no-unused-expressions" rule for eslint (Miroslav Bajtoš)

 * Update eslint to loopback config v5 (Loay)

 * Fix total calculation in example (Candy)

 * make test individually runable (David Cheung)

 * Add options to bulkUpdate (Kogulan Baskaran)

 * Fix context within listByPrincipalType role method (codyolsen)

 * Add Node v7 to Travis CI platforms (Miroslav Bajtoš)

 * Drop support for Node v0.10 and v0.12 (Miroslav Bajtoš)

 * Add templateFn option to User#verify() (Adrien Kiren)

 * Require verification after email change (Loay)

 * Update doc links (Candy)

 * adding check of string for case insensitive emails (Dhaval Trivedi)

 * Update test confirmation text in PR template (#2897) (Simon Ho)

 * allow batch create for persisted models (David Cheung)

 * Fix PR template to not link all PRs to #49 (#2887) (Miroslav Bajtoš)

 * Need index on principalId for performance. (#2883) (Simon Ho)

 * Remove redundant items in PR template (#2877) (Simon Ho)

 * Refactor PR template based on feedback (#2865) (Simon Ho)

 * Add pull request template (#2843) (Simon Ho)

 * Update README.md (Rand McKinney)

 * Reword ticking checkbox note in issue template (#2854) (Simon Ho)

 * Add how to tick checkbox in issue template (#2851) (Simon Ho)

 * Fix description of updateAll response (Miroslav Bajtoš)

 * Allow tokens with eternal TTL (value -1) (Miroslav Bajtoš)

 * Use GitHub issue templates (#2810) (Simon Ho)

 * Update ja and nl translation files (Candy)

 * Remove 3.0 DEVELOPING & RELEASE-NOTES (Miroslav Bajtoš)

 * Fix support for remote hooks returning a Promise (Tim van der Staaij)

 * Validate non-email property partial update (Loay)

 * Update release notes (Amir Jafarian)

 * Update translation files - round#2 (Candy)

 * Add license text (Candy)

 * Temporarily disable Karma tests on Windows CI (Miroslav Bajtoš)


2016-09-22, Version 3.0.0
=========================

 * Update deps to 3.0.0 RC (Miroslav Bajtoš)

 * Update globalization structure (Candy)

 * Call new disable remote method from model class. (Richard Pringle)

 * Add translation strings (Candy)

 * Support uniqueness for realm users (David Cheung)

 * Invalidate sessions after email change (Loay)

 * Add docs for KeyValue model (Simon Ho)

 * Fix remote method inheritance (Candy)

 * Fix double-slash in confirmation URL (Miroslav Bajtoš)


2016-09-09, Version 3.0.0-alpha.5
=================================

 * Use strong-remoting's new TypeRegistry (Miroslav Bajtoš)

 * test/user: don't attach User model twice (Miroslav Bajtoš)

 * app.enableAuth: correctly detect attached models (Miroslav Bajtoš)

 * Fix remoting metadata for "data" arguments (Miroslav Bajtoš)

 * Add instructions for upgrading context (Miroslav Bajtoš)

 * Discard sugar method for model creation (gunjpan)

 * Remove one-var exceptions no longer needed (Miroslav Bajtoš)

 * Rework email validation to use isemail (Miroslav Bajtoš)

 * Expose upsertWithWhere method (Sonali Samantaray)


2016-09-05, Version 3.0.0-alpha.4
=================================

 * Update loopback-connector-remote to 2.0-alpha (Miroslav Bajtoš)

 * Add remoting for KeyValue model TTL feature (Simon Ho)

 * Add lint NPM script (Simon Ho)

 * Make the app instance available to connectors (Subramanian Krishnan)

 * Update pre-release dependencies (Miroslav Bajtoš)

 * Apply g.f to literal strings (Setogit)

 * Allow resetPassword if  emailVerified (Loay)

 * Reorder PATCH Vs PUT endpoints (Amir Jafarian)

 * streamline use if `self` (Benjamin Kroeger)

 * resolve related models from correct registry (Benjamin Kroeger)

 * KeyValueModel: add API for listing keys (Miroslav Bajtoš)

 * Fix token middleware crash (Carl Fürstenberg)

 * loopback#context: fix missing "g" symbol (Miroslav Bajtoš)

 * Update acl.js (Rand McKinney)

 * Support 'alias' in mail transport config. (Samuel Reed)

 * Remove unnecessary g.log (Setogit)

 * Revert globalization of Swagger descriptions (Miroslav Bajtoš)

 * Revert globalization of assert() messages (Miroslav Bajtoš)

 * Add bcrypt validation (Loay)


2016-08-11, Version 3.0.0-alpha.3
=================================

 * common: add KeyValueModel (Miroslav Bajtoš)

 * Globalize current-context error messages (Miroslav Bajtoš)

 * Remove current-context API (Miroslav Bajtoš)

 * Fix forceId in tests (jannyHou)

 * test: increase timeout to prevent CI failures (Miroslav Bajtoš)

 * Update globalization string (Candy)

 * Update globalization (Candy)

 * Add globalization (Candy)

 * test: fix "socket hang up" error in app.test (Miroslav Bajtoš)

 * test: increate timeout in Role test (Miroslav Bajtoš)

 * test: make status test more robust (Miroslav Bajtoš)

 * test: fix broken Role tests (Miroslav Bajtoš)

 * Update dependencies to their latest versions (Miroslav Bajtoš)

 * Increase timeout (jannyHou)

 * test: fix change-tracking setup (Miroslav Bajtoš)

 * test: use local registry in test fixtures (Miroslav Bajtoš)

 * Update loopback.js (Rand McKinney)

 * Fix test case error (Loay)

 * Update user.js (Loay)

 * Fix security issue 580 (Loay)

 * Update URLs in CONTRIBUTING.md (#2503) (Ryan Graham)

 * Remove legacyExplorer (gunjpan)

 * Remove `rectifyAllChanges` and `rectifyChange` (Candy)

 * Fix verificationToken bug (Loay)

 * update express version (Loay)

 * Cleanup unit-test added in 1fc51d129 (Miroslav Bajtoš)

 * update errorHandler template (Loay)


2016-06-13, Version 3.0.0-alpha.2
=================================

 * add missing unit tests for #2108 (Benjamin Kroeger)

 * Expose `Replace*` methods (Amir Jafarian)

 * Update tests for strong-error-handler (David Cheung)

 * Remove legacy express 3.x middleware getters (Miroslav Bajtoš)

 * Docuemtation for `replace*` methods (Amir Jafarian)

 * Make the doc clear for `findORCreate` cb (Amir Jafarian)

 * Fix JSCS unsupported rule error (Jason)

 * Remove env.json and strong-pm dir (Ritchie Martori)

 * Throw error upon extending unknown model (David Cheung)

 * Remove unused UserModel properties (David Cheung)

 * Remove Change.handleError (Candy)

 * Update user.js (Rik)

 * Separate error-checking and next/done logic from other logic in the test suite (Supasate Choochaisri)

 * Clean up by removing unnecessary comments (Supasate Choochaisri)

 * Add feature to not allow duplicate role name (Supasate Choochaisri)

 * update copyright statements (Ryan Graham)

 * relicense as MIT only (Ryan Graham)

 * Upgrade phantomjs to 2.x (Miroslav Bajtoš)

 * app: send port:0 instead of port:undefined (Miroslav Bajtoš)

 * travis: drop node@5, add node@6 (Miroslav Bajtoš)

 * Disable DEBUG output for eslint on Jenkins CI (Miroslav Bajtoš)

 * Remove "loopback.autoAttach()" (Miroslav Bajtoš)

 * test/rest.middleware: use local registry (Miroslav Bajtoš)

 * Fix role.isOwner to support app-local registry (Miroslav Bajtoš)

 * test/user: use local registry (Miroslav Bajtoš)

 * Resolver support return promise (juehou)

 * remove @private from jsdoc (Manu Phatak)

 * Fixes for emit `remoteMethodDisabled` PR (Simon Ho)

 * Add new feature to emit a `remoteMethodDisabled` event when disabling a remote method. (Supasate Choochaisri)

 * Fix typo in Model.nestRemoting (Tim Needham)

 * Update loopback.js (Rand McKinney)

 * Allow built-in token middleware to run repeatedly (Benjamin Kröger)

 * Use eslint with loopback config (Miroslav Bajtoš)

 * promise docs (Jue Hou)

 * Update JSDoc (sghung@ca.ibm.com)

 * Remove constraint making isStatic required (Candy)

 * Fix inconsistencies in JSDoc (sghung@ca.ibm.com)

 * Improve error message on connector init error (Miroslav Bajtoš)

 * application: correct spelling of "cannont" (Sam Roberts)

 * Remove sl-blip from dependency (Candy)

 * Use new strong-remoting API (Candy)

 * test: remove forgotten console.trace logs (Miroslav Bajtoš)

 * Fix race condition in replication tests (Miroslav Bajtoš)

 * Fix race condition in error handler test (Miroslav Bajtoš)

 * test: remove errant console.log from test (Ryan Graham)

 * Promisify Model Change (Jue Hou)

 * Travis: drop iojs, add v4.x and v5.x (Miroslav Bajtoš)

 * test: use ephemeral port for e2e server (Ryan Graham)

 * test: fail on error instead of crash (Ryan Graham)

 * ensure app is booted before integration tests (Ryan Graham)

 * Remove "loopback.DataModel" (Miroslav Bajtoš)

 * Correct JSDoc findOrCreate() callback in PersistedModel (Chris Coggburn)

 * Fix typo in package.json (publishConfig) (Miroslav Bajtoš)

 * Start development of 3.0 (Candy)

 * Hide verificationToken (Samuel Gaus)

 * Fix description for User.prototype.hasPassword (Jue Hou)

 * Checkpoint speedup (Amir Jafarian)

 * Always use bluebird as promise library Replace `global.Promise` with `bluebird` (Jue Hou)

 * Remove unused code from loopback-testing-helper (Simon Ho)

 * Make juggler a regular dependency (Miroslav Bajtoš)

 * Remove dependency on loopback-testing (Simon Ho)

 * Fix failing tests (Simon Ho)

 * Update persisted-model.js (Rand McKinney)

 * Update persisted-model.js (linguofeng)


2015-12-22, Version 3.0.0-alpha.1
=================================

 * Update juggler to ^3.0.0-alpha.1 (Miroslav Bajtoš)

 * Start development of 3.0 (Miroslav Bajtoš)


2015-12-22, Version 2.26.2
==========================

 * Fix bulkUpdate to not trigger rectifyAll (Amir Jafarian)


2015-12-17, Version 2.26.1
==========================

 * PersistedModel: log rectify/rectifyAll triggers (Miroslav Bajtoš)


2015-12-09, Version 2.26.0
==========================

 * change: skip cp lookup on no change (Miroslav Bajtoš)

 * Change: correctly rectify no-change (Miroslav Bajtoš)

 * Update model.js (Rand McKinney)

 * Adding properties description for User Model (David Cheung)

 * Add case-sensitve email option for User model. (Richard Pringle)


2015-11-13, Version 2.25.0
==========================

 * Fix typo in description of persistedModel.updateAttributes() (Richard Pringle)


2015-11-09, Version 2.24.0
==========================

 * Fix cookie-parser error (Simon Ho)


2015-11-09, Version 2.23.0
==========================

 * lib/registry: fix findModel for model ctor (Miroslav Bajtoš)

 * Refer to licenses with a link (Sam Roberts)

 * Fix user.resetPassword to fail on email not found (Simo Moujami)

 * Fix typo in doc comment (Rand McKinney)

 * Do not include redundant ports in verify links (Samuel Gaus)

 * Set application's id property only if it's empty. (wusuopu)

 * Check configs for shared method settings (Simon Ho)

 * Add test fixtures for shared methods (Simon Ho)

 * Clean up .jshintrc (Simon Ho)

 * Update comment about user ACL to reflect implementation (Felipe Oliveira Carvalho)


2015-09-23, Version 2.22.2
==========================

 * Use strongloop conventions for licensing (Sam Roberts)

 * Set package license to MIT (Sam Roberts)


2015-09-18, Version 2.22.1
==========================

 * Fix perf of rectification after updateAttributes (Miroslav Bajtoš)

 * Update persisted-model.js (Rand McKinney)

 * Stop NPM license warning (Simon Ho)


2015-09-03, Version 2.22.0
==========================

 * Create stack-removing errorhandler middleware (Richard Walker)

 * Update README.md (Rand McKinney)

 * Allow EJS templates to use includes (Samuel Gaus)

 * Fix options.to assertion message in user.verify (Farid Nouri Neshat)

 * Upgrade Travis to container-based infrastructure (Miroslav Bajtoš)

 * fix typo "PeristedModel" (Christoph)


2015-08-13, Version 2.21.0
==========================

 * Add util methods to ACL and clean up related model resolutions (Raymond Feng)

 * Promisify 'PersistedModel - replication' (Pradnya Baviskar)

 * Promisify 'Application' model (Pradnya Baviskar)


2015-08-06, Version 2.20.0
==========================

 * Allow methods filter for middleware config (Raymond Feng)

 * Don't load Bluebird for createPromiseCallback (Miroslav Bajtoš)

 * fix exit early when password is non-string closes #1437 (Berkeley Martinez)

 * Promisify User model (Pradnya Baviskar)

 * Add missing . to user model property descriptions (Richard Walker)


2015-07-28, Version 2.19.1
==========================

 * Disable application model test for karma (Raymond Feng)

 * Fix jsdocs for methods with where argument (Raymond Feng)

 * Add link to createChangeStream docs (Ritchie Martori)


2015-07-09, Version 2.19.0
==========================

 * Add PersistedModel.createChangeStream() (Ritchie Martori)

 * Remove trailing whitespace from jsdoc (Ritchie Martori)

 * Update model.js (Rand McKinney)

 * Downgrade version of loopback-testing (Ritchie Martori)

 * Auto-configure models required by `app.enableAuth` (Miroslav Bajtoš)

 * Add loadBuiltinModels flag to loopback(options) (Miroslav Bajtoš)

 * Add a unit-test for searchDefaultTokenKeys (Miroslav Bajtoš)

 * access-token: add option "searchDefaultTokenKeys" (Owen Brotherwood)

 * Fix the test case (Raymond Feng)

 * Fix code standards issues (Tom Kirkpatrick)

 * Add test case to highlight fatal error when trying to include a scoped relationship through a polymorphic relationship (Tom Kirkpatrick)

 * add callback args for listByPrincipalType to jsdoc comment, pass explicit arguments to callback (Esco Obong)

 * mark utiltiy function as private (Esco Obong)

 * fix linting errors (Esco Obong)

 * fix lint erros (Esco Obong)

 * consolidate Role methods roles, applications, and users into one, add query param to allow for pagination and restricting fields (Esco Obong)

 * fix implementation of Role methods: users,roles, and applications (Esco Obong)


2015-05-13, Version 2.18.0
==========================

 * Make the test compatible with latest juggler (Raymond Feng)


2015-05-12, Version 2.17.3
==========================

 * Use the new remoting.authorization hook for check access (Ritchie Martori)

 * Define remote methods via model settings/config (Miroslav Bajtoš)

 * Pass the full options object to the email send method in user verification process. (Alexandru Savin)

 * un-document _findLayerByHandler (Rand McKinney)

 * Gruntfile: disable debug & watch for CI builds (Miroslav Bajtoš)

 * Update devDependencies to the latest versions (Miroslav Bajtoš)

 * Remove trailing whitespace added by 242bcec (Miroslav Bajtoš)

 * Update model.js (Rand McKinney)


2015-04-28, Version 2.17.2
==========================

 * Fix regression in Model.getApp() (Miroslav Bajtoš)


2015-04-28, Version 2.17.1
==========================

 * Allow dataSource === false (Raymond Feng)

 * Fix remoting metadata for User.login#include (Miroslav Bajtoš)


2015-04-21, Version 2.17.0
==========================

 * Disable inclusion of User.accessTokens (Raymond Feng)

 * Upgrade test fixtures to use LB 2.x layout (Raymond Feng)


2015-04-17, Version 2.16.3
==========================

 * Rework global registry to be per-module-instance (Miroslav Bajtoš)


2015-04-17, Version 2.16.1
==========================

 * Add back loopback properties like modelBuilder (Miroslav Bajtoš)


2015-04-16, Version 2.16.0
==========================

 * Expose the `filter` argument for findById (Raymond Feng)

 * fixed the missing '.' in various description fields. (Edmond Lau)

 * Conflict resolution and Access control (Miroslav Bajtoš)

 * Fix the typo (Raymond Feng)

 * Fix PersistedModel._defineChangeModel (Miroslav Bajtoš)

 * AccessControl for change replication (Miroslav Bajtoš)

 * test: remove global autoAttach (Miroslav Bajtoš)

 * Add support for app level Model isolation (Ritchie Martori)

 * Implement ModelCtor.afterRemoteError (Miroslav Bajtoš)

 * Code cleanup, add Model._runWhenAttachedToApp (Miroslav Bajtoš)

 * Refactor Model and PersistedModel registration (Miroslav Bajtoš)

 * Fix the style issue (Raymond Feng)

 * Add missing error handlers to checkpoints() (Miroslav Bajtoš)

 * Fix where param format (Rand McKinney)

 * Test embedsOne CRUD methods (Fabien Franzen)


2015-04-01, Version 2.15.0
==========================

 * Improve error handling in replication (Miroslav Bajtoš)

 * Add `loopback.runInContext` (Miroslav Bajtoš)

 * Fix style issues (Raymond Feng)

 * Document the new third callback arg of replicate() (Miroslav Bajtoš)

 * Fix API doc for updateAll/deleteAll (Miroslav Bajtoš)

 * Import subset of underscore.string scripts only (Miroslav Bajtoš)

 * Use `ctx.instance` provided by "after delete" hook (Miroslav Bajtoš)

 * Add conflict resolution API (Miroslav Bajtoš)

 * Detect 3rd-party changes made during replication (Miroslav Bajtoš)

 * Ability to pass in custom verification token generator This commit adds the ability for the developer to use a custom token generator function for the user.verify(...) method. By default, the system will still use the crypto.randomBytes() method if no option is provided. (jakerella)

 * Remove unnecessary delay in tests. (Miroslav Bajtoš)

 * Update README.md (Simon Ho)

 * Remove duplicate cb func from getRoles and other doc cleanup (crandmck)

 * Enhance the token middleware to support current user literal (Raymond Feng)

 * Handling owner being a relation/function (Benjamin Boudreau)

 * Run replication tests in the browser too (Miroslav Bajtoš)

 * Add replication tests for conflict resolution (Miroslav Bajtoš)

 * Fix an assertion broke by recent chai upgrade. (Miroslav Bajtoš)

 * Static ACL support array of properties now (ulion)

 * Add more integration tests for replication (Miroslav Bajtoš)

 * Prevent more kinds of false replication conflicts (Miroslav Bajtoš)

 * Upgrade deps (Raymond Feng)

 * Fix "Issues" link in readme (Simon Ho)

 * Add more debug logs to replication (Miroslav Bajtoš)

 * Fixes #1158. (Jason Sturges)

 * Checkpoint: start with seq=1 instead of seq=0 (Miroslav Bajtoš)

 * Return new checkpoints in callback of replicate() (Miroslav Bajtoš)

 * Create a remote checkpoint during replication too (Miroslav Bajtoš)

 * Replication: fix checkpoint-related race condition (Miroslav Bajtoš)

 * Support different "since" for source and target (Miroslav Bajtoš)


2015-03-03, Version 2.14.0
==========================

 * Replace deprecated hooks with Operation hooks (Miroslav Bajtoš)

 * test: don't warn about running deprecated paths (Miroslav Bajtoš)

 * karma conf: prevent timeouts on Travis CI (Miroslav Bajtoš)

 * Pass options from User.login to createAccessToken (Raymond Feng)

 * Config option to disable legacy explorer routes Setting legacyExplorer to false in the loopback config will disable the routes /routes and /models made available in loopback.rest. The deprecate module has been added to the project with a reference added for the legacyExplorer option as it is no longer required by loopback-explorer. Tests added to validate functionality of disabled and enabled legacy explorer routes. (Ron Edgecomb)

 * test: setup GUID for all models tracking changes (Miroslav Bajtoš)

 * Change tracking requires a string id set to GUID (Miroslav Bajtoš)


2015-02-25, Version 2.13.0
==========================

 * Add a workaround to avoid conflicts with NewRelic (Raymond Feng)

 * Fix "User.confirm" to always call afterRemote hook (Pradnya Baviskar)

 * Skip hashing password if it's already hashed (Raymond Feng)

 * travis.yml: drop 0.11, add 0.12 and iojs (Miroslav Bajtoš)

 * Add docs for settings per #1069 (crandmck)

 * Fix change detection & tracking (Miroslav Bajtoš)

 * Minor doc fix (Ritchie Martori)

 * Upgrade jscs to ~1.11 via grunt-jscs ^1.5 (Miroslav Bajtoš)

 * Remove redundant dev-dep serve-favicon (Miroslav Bajtoš)

 * Fix test broken by recent juggler changes (Miroslav Bajtoš)

 * Fix coding style issue (Raymond Feng)

 * Remove trailing spaces (Raymond Feng)

 * Fix for issue 1099. (zane)

 * Fix API docs per #1041 (crandmck)

 * Fix API docs to add proper callback doc per #1041 (crandmck)

 * Fix #1080 - domain memory leak. (Samuel Reed)

 * Document user settings (Ritchie Martori)

 * Add wiki references to readme (Simon Ho)


2015-02-03, Version 2.12.1
==========================

 * Map not found to 404 for hasOne (Raymond Feng)


2015-02-03, Version 2.12.0
==========================

 * Fix the test case (Raymond Feng)

 * Enable remoting for hasOne relations (Raymond Feng)

 * README: add Gitter badge (Miroslav Bajtoš)


2015-01-27, Version 2.11.0
==========================

 * Document options for persistedmodel.save() (Rand McKinney)

 * Add test case to demonstrate url-encoded http path (Pradnya Baviskar)

 * Fix JSdocs per #888 (crandmck)

 * Add test case for loopback issue #698 (Pradnya Baviskar)

 * Remove usages of deprecated `req.param()` (Miroslav Bajtoš)

 * Add error code property to known error responses. (Ron Edgecomb)

 * test: use 127.0.0.1 instead of localhost (Ryan Graham)

 * Extend AccessToken to parse Basic auth headers (Ryan Graham)

 * tests: fix Bearer token test (Ryan Graham)

 * don't send queries to the DB when no changes are detected (bitmage)


2015-01-16, Version 2.10.2
==========================

 * Make sure EXECUTE access type matches READ or WRITE (Raymond Feng)


2015-01-15, Version 2.10.1
==========================

 * Optimize the creation of handlers for rest (Raymond Feng)

 * Add a link to gitter chat (Raymond Feng)

 * Added context middleware (Rand McKinney)

 * Use User.remoteMethod instead of loopbacks method This is needed for loopback-connector-remote authorization. Addresses https://github.com/strongloop/loopback/issues/622. (Berkeley Martinez)


2015-01-07, Version 2.10.0
==========================

 * Revert the peer dep change to avoid npm complaints (Raymond Feng)

 * Update strong-remoting dep (Raymond Feng)

 * Allow accessType per remote method (Raymond Feng)

 * API and REST tests added to ensure complete and valid credentials are supplied for verified error message to be returned  - tests added as suggested and fail under previous version of User model  - strongloop/loopback#931 (Ron Edgecomb)

 * Require valid login credentials before verified email check.  - strongloop/loopback#931. (Ron Edgecomb)


2015-01-07, Version 2.9.0
=========================

 * Update juggler dep (Raymond Feng)

 * Fix Geo test cases (Raymond Feng)

 * Allow User.hashPassword/validatePassword to be overridden (Raymond Feng)


2015-01-07, Version 2.8.8
=========================

 * Fix context middleware to preserve domains (Pham Anh Tuan)

 * Additional password reset unit tests for API and REST  - strongloop/loopback#944 (Ron Edgecomb)

 * Small formatting update to have consistency with identical logic in other areas.   - strongloop/loopback#944 (Ron Edgecomb)

 * Simplify the API test for invalidCredentials (removed create), move above REST calls for better grouping of tests   - strongloop/loopback#944 (Ron Edgecomb)

 * Force request to send body as string, this ensures headers aren't automatically set to application/json  - strongloop/loopback#944 (Ron Edgecomb)

 * Ensure error checking logic is in place for all REST calls, expand formatting for consistency with existing instances.  - strongloop/loopback#944 (Ron Edgecomb)

 * Correct invalidCredentials so that it differs from validCredentialsEmailVerified, unit test now passes as desired.  - strongloop/loopback#944 (Ron Edgecomb)

 * Update to demonstrate unit test is actually failing due to incorrect values of invalidCredentials  - strongloop/loopback#944 (Ron Edgecomb)

 * fix jscs warning (Clark Wang)

 * fix nestRemoting is nesting hooks from other relations (Clark Wang)


2015-01-06, Version 2.8.7
=========================

 * Change urlNotFound.js to url-not-found.js (Rand McKinney)

 * Add lib/server-app.js (Rand McKinney)

 * package: add versioned sl-blip dependency (Ryan Graham)

 * fix User.settings.ttl can't be overridden in sub model (Clark Wang)

 * Fix Change.getCheckpointModel() giving new models each call (Farid Neshat)

 * Update README.md (Rand McKinney)


2014-12-15, Version 2.8.6
=========================

 * server-app: make _sortLayersByPhase stable (Miroslav Bajtoš)

 * Rework phased middleware, fix several bugs (Miroslav Bajtoš)


2014-12-12, Version 2.8.5
=========================

 * fix jshint errors (Clark Wang)

 * test if cb exists (Clark Wang)

 * fix nested remoting function throwing error will crash app (Clark Wang)

 * Fix bcrypt issues for browserify (Raymond Feng)


2014-12-08, Version 2.8.4
=========================

 * Allow native bcrypt for performance (Raymond Feng)


2014-12-08, Version 2.8.3
=========================

 * Remove unused underscore dependency (Ryan Graham)


2014-11-27, Version 2.8.2
=========================

 * Prepend slash for nested remoting paths (Clark Wang)

 * fix jscs errors (Rob Halff)

 * enable jshint for tests (Rob Halff)

 * permit some globals (Rob Halff)

 * 'done' is not defined (Rob Halff)

 * 'memory' is already defined (Rob Halff)

 * singlequote, semicolon & /*jshint -W030 */ (Rob Halff)


2014-11-25, Version 2.8.1
=========================

 * Update docs.json (Rand McKinney)

 * Update favicon.js (Rand McKinney)


2014-11-19, Version 2.8.0
=========================

 * Expose more loopback middleware for require (Raymond Feng)

 * Scope app middleware to a list of paths (Miroslav Bajtoš)

 * Update CONTRIBUTING.md (Alex Voitau)

 * Fix the model name for hasMany/through relation (Raymond Feng)

 * Fixing the model attach (wfgomes)

 * Minor: update jsdoc for PersistedModel.updateAll (Alex Voitau)

 * AccessToken: optional `options` in findForRequest (Miroslav Bajtoš)

 * server-app: improve jsdoc comments (Miroslav Bajtoš)

 * server-app: middleware API improvements (Miroslav Bajtoš)

 * typo of port server (wfgomes)

 * Move middleware sources to `server/middleware` (Miroslav Bajtoš)

 * app.middleware: verify serial exec of handlers (Miroslav Bajtoš)

 * Simplify `app.defineMiddlewarePhases` (Miroslav Bajtoš)

 * Make sure loopback has all properties from express (Raymond Feng)

 * Implement `app.defineMiddlewarePhases` (Miroslav Bajtoš)

 * Implement app.middlewareFromConfig (Miroslav Bajtoš)

 * middleware/token: store the token in current ctx (Miroslav Bajtoš)

 * Fix `loopback.getCurrentContext` (Miroslav Bajtoš)

 * Update chai to ^1.10.0 (Miroslav Bajtoš)

 * package: fix deps (Miroslav Bajtoš)

 * Middleware phases - initial implementation (Miroslav Bajtoš)

 * Allows ACLs/settings in model config (Raymond Feng)

 * Remove context middleware per Ritchie (Rand McKinney)

 * Add API doc for context middleware - see #337 (crandmck)

 * Update persisted-model.js (Rand McKinney)

 * rest middleware: clean up context config (Miroslav Bajtoš)

 * Move `context` example to a standalone app (Miroslav Bajtoš)

 * Enable the context middleware from loopback.rest (Raymond Feng)

 * Add context propagation middleware (Raymond Feng)

 * Changes to JSdoc comments (Rand McKinney)

 * Reorder classes alphabetically in each section (Rand McKinney)

 * common: coding style cleanup (Miroslav Bajtoš)

 * Coding style cleanup (Gruntfile, lib) (Miroslav Bajtoš)

 * Enable jscs for `lib`, fix style violations (Rob Halff)

 * Add access-context.js to API doc (Rand McKinney)

 * Remove doc for debug function (Rand McKinney)

 * Update registry.js (Rand McKinney)

 * Fix the jsdoc for User.login (Raymond Feng)

 * Deleted instantiation of new Change model. This PR removes the instantiation of a new change model as models return from Change.find are already instances of Change. This solves the duplicate Id issue #649 (Berkeley Martinez)

 * Expose path to the built-in favicon file (Miroslav Bajtoš)

 * Add API docs for `loopback.static`. (Miroslav Bajtoš)

 * Add test for `remoting.rest.supportedTypes` (Miroslav Bajtoš)

 * Revert "rest handler options" (Miroslav Bajtoš)

 * REST handler options. (Guilherme Cirne)

 * The elapsed time in milliseconds can be 0 (less than 1 ms) (Raymond Feng)


2014-10-27, Version 2.7.0
=========================

 * Bump version (Raymond Feng)

 * User: custom email headers in verify (Juan Pizarro)

 * Add realm support (Raymond Feng)

 * Make sure GET /:id/exists returns 200 {exists: true|false} https://github.com/strongloop/loopback/issues/679 (Raymond Feng)

 * Adjust id handling to deal with 0 and null (Chris S)

 * Force principalId to be a string. (Chris S)


2014-10-23, Version 2.6.0
=========================

 * User: fix `confirm` permissions (Miroslav Bajtoš)

 * Use === to compare with 0 (Rob Halff)

 * add laxbreak option (Rob Halff)

 * use singlequotes (Rob Halff)

 * split jshint task for test & lib (Rob Halff)

 * allow comma first style and increase line length (Rob Halff)

 * add missing semicolons (Rob Halff)

 * Support per-model and per-handler remoting options (Fabien Franzen)

 * Fix JSdoc for registerResolver (Rand McKinney)

 * lib/application: improve URL building algo (Miroslav Bajtoš)

 * Fix findById callback signature (Rand McKinney)

 * JSdoc fixes (Rand McKinney)

 * Fix places using undefined variables (Miroslav Bajtoš)

 * Clean up jsdoc comments (crandmck)

 * models: move Change LDL def into a json file (Miroslav Bajtoš)

 * models: move Checkpoint LDL def into a json file (Miroslav Bajtoš)

 * models: move Role LDL def into a json file (Miroslav Bajtoš)

 * models: move RoleMapping def into its own files (Miroslav Bajtoš)

 * models: move ACL LDL def into a json file (Miroslav Bajtoš)

 * models: move Scope def into its own files (Miroslav Bajtoš)

 * models: move AccessToken LDL def into a json file (Miroslav Bajtoš)

 * models: move Application LDL def into a json file (Miroslav Bajtoš)

 * models: move Email LDL def into `email.json` (Miroslav Bajtoš)

 * models: move User LDL def into `user.json` (Miroslav Bajtoš)

 * test: run more tests in the browser (Miroslav Bajtoš)

 * test: verify exported models (Miroslav Bajtoš)

 * test: remove infinite timeout (Miroslav Bajtoš)

 * Auto-load and register built-in `Checkpoint` model (Miroslav Bajtoš)

 * Skip static ACL entries that don't match the property (Raymond Feng)

 * Dismantle `lib/models`. (Miroslav Bajtoš)

 * Register built-in models in a standalone file (Miroslav Bajtoš)


2014-10-10, Version 2.4.1
=========================

 * models/change: fix `id` property definition (Miroslav Bajtoš)

 * Added class properties jsdoc. (Rand McKinney)

 * Fixed up JS Doc (Rand McKinney)

 * Update contribution guidelines (Ryan Graham)

 * Document ACL class properties (Rand McKinney)

 * Add properties JSdoc. (Rand McKinney)

 * Move looback remote connector to npm module (Krishna Raman)

 * Update strong-remoting version (Ritchie Martori)

 * Document user class properties (Ritchie Martori)

 * Add Model.disableRemoteMethod() (Ritchie Martori)


2014-09-12, Version 2.2.0
=========================

 * Bump versions (Raymond Feng)

 * PersistedModel: add remote method aliases (Miroslav Bajtoš)

 * Fix last commit, which misplaced an ACL. Move the ACL inside "acls". Signed-off-by: Carey Richard Murphey <rich@murphey.org> (zxvv)

 * Add an ACL to User, to allow everyone to execute User.passwordReset(). (zxvv)

 * package: add "web" keyword (Miroslav Bajtoš)

 * Fix require (Fabien Franzen)

 * Fix coercion for remoting on vanilla models (Ritchie Martori)

 * user#login include server crash fix (Alexander Ryzhikov)

 * Update model.js (Rand McKinney)

 * Restrict: only hasManyThrough relation can have additional properties (Clark Wang)

 * Restrict that only hasManyThrough can have additional properties (Clark Wang)

 * Add tests for hasManyThrough link with data (Clark Wang)

 * Support data field as body for link operation (Clark Wang)

 * Tiny fix: correct url format (Fabien Franzen)

 * Fix embedsMany/findById to return proper 404 response (Fabien Franzen)

 * registry: warn when dataSource is not specified (Miroslav Bajtoš)

 * Only validate dataSource when defined (Fixes #482) (Ritchie Martori)

 * Fix tests (Fabien Franzen)

 * Enable remoting for embedsOne relation (Jaka Hudoklin)

 * Allow 'where' argument for scoped count API (Fabien Franzen)

 * Account for undefined before/afterListeners (Fabien Franzen)

 * added test and fixed changing passed in object within ctor (britztopher)

 * adding the ability to use single or multiple email transports in datasources.json file (britztopher)

 * added the ability to use an array of transports or just a single trnasport (britztopher)


2014-08-18, Version 2.1.3
=========================

 * Bump version (Raymond Feng)

 * Make sure AccessToken extends from PersistedModel (Raymond Feng)

 * add count to relations and scopes (Jaka Hudoklin)

 * Remove `req.resume` from `app.enableAuth` (Miroslav Bajtoš)

 * Fix accessToken property docs (Ritchie Martori)


2014-08-08, Version 2.1.1
=========================

 * Bump version (Raymond Feng)

 * Make sure scoped methods are remoted (Raymond Feng)

 * Pass in remotingContext for ACL (Raymond Feng)

 * Fix reference to app (Raymond Feng)

 * Don't assume relation.modelTo in case of polymorphic belongsTo (Fabien Franzen)


2014-08-07, Version 2.1.0
=========================

 * Bump version (Raymond Feng)

 * Fix doc for the EXECUTE (Raymond Feng)

 * Fix "callbacl" by "callback" in doc (Steve Grosbois)

 * Inherit hooks when nesting (Fabien Franzen)

 * Changed options.path to options.http.path (Fabien Franzen)

 * filterMethod can also be a direct callback (Fabien Franzen)

 * filterMethod option (fn) to filter nested remote methods (Fabien Franzen)

 * Fix test to be more specific (Fabien Franzen)

 * Implement Model.nestRemoting (Fabien Franzen)

 * Allow custom relation path (http) - enable hasOne remoting access (Fabien Franzen)

 * Expose Model.exists over HTTP HEAD (Raymond Feng)

 * Return data source for app.dataSource() (Raymond Feng)

 * Fix typo in README (Ritchie Martori)

 * Integration test: referencesMany (Fabien Franzen)

 * Integration test: embedsMany (Fabien Franzen)

 * Fix jsdoc for remoteMethod() (Rand McKinney)

 * Map exists to HEAD for REST (Raymond Feng)

 * Build the email verification url from app context (Raymond Feng)


2014-07-27, Version 2.0.2
=========================

 * Fix https://github.com/strongloop/loopback/issues/413 (Raymond Feng)

 * Update test case to remove usage of deprecated express apis (Raymond Feng)


2014-07-26, Version 2.0.1
=========================

 * Bump version (Raymond Feng)

 * updated LB module diagram (altsang)

 * Update package.json (Al Tsang)

 * Updates for 2.0 (crandmck)

 * Update module diagram again (crandmck)

 * Update module diagram (crandmck)

 * Emit a 'modelRemoted' event by app.model() (Raymond Feng)

 * Fix remoting types for related models (Raymond Feng)

 * Fix for email transports (Raymond Feng)

 * Remove the link to obsolete wiki page to favor loopback.io (Raymond Feng)


2014-07-22, Version 2.0.0
=========================

 * Enhance the base model assertions (Raymond Feng)

 * Report error for User.confirm() (Raymond Feng)

 * Set up the base model based on the connector types (Raymond Feng)

 * express-middleware: improve error message (Miroslav Bajtoš)

 * Remove `app.docs()` (Miroslav Bajtoš)

 * Remove `loopback.compat.usePluralNamesForRemoting` (Miroslav Bajtoš)

 * Validate username uniqueness (Jaka Hudoklin)

 * Add descriptions for custom methods on user model (Raymond Feng)

 * Move remoting metadata from juggler to loopback (Raymond Feng)

 * Upgrade to nodemailer 1.0.1 (Raymond Feng)

 * Enhance the error message (Raymond Feng)


2014-07-16, Version 2.0.0-beta7
===============================

 * Bump version (Raymond Feng)

 * Remove unused dep (Raymond Feng)

 * Bump version and update deps (Raymond Feng)

 * Upgrade to loopback-datasource-juggler@1.7.0 (Raymond Feng)

 * Refactor modelBuilder to registry and set up default model (Raymond Feng)

 * Add a test case for credentials/challenges (Raymond Feng)

 * Fix credentials/challenges types (Raymond Feng)

 * Update modules for examples (Raymond Feng)

 * Split out aliases for deleteById and destroyAll functions for jsdoc. (crandmck)

 * Remove unused deps (Raymond Feng)

 * Refactor email verification tests into a new group (Raymond Feng)

 * Fix the typo (Raymond Feng)

 * Add an option to honor emailVerified (Raymond Feng)

 * Update module list in README (Raymond Feng)

 * Refine the test cases for relation REST APIs (Raymond Feng)

 * test: add check of Model remote methods (Miroslav Bajtoš)

 * Adjust the REST mapping for add/remove (Raymond Feng)

 * Add a test case for hasMany through add/remove remoting (Raymond Feng)

 * Fix the typo and add Bearer token support (Raymond Feng)

 * Update README (Raymond Feng)

 * Fix misleading token middleware documentation (Aleksandr Tsertkov)


2014-07-15, Version 2.0.0-beta6
===============================

 * lib/application: publish Change models to REST API (Miroslav Bajtoš)

 * models/change: fix typo (Miroslav Bajtoš)

 * checkpoint: fix `current()` (Miroslav Bajtoš)


2014-07-03, Version 2.0.0-beta5
===============================

 * app: update `url` on `listening` event (Miroslav Bajtoš)

 * Fix "ReferenceError: loopback is not defined" in registry.memory(). (Guilherme Cirne)

 * Invalid Access Token return 401 (Karl Mikkelsen)

 * Bump version and update deps (Raymond Feng)

 * Update debug setting (Raymond Feng)

 * Mark `app.boot` as deprecated. (Miroslav Bajtoš)

 * Update link to doc (Rand McKinney)


2014-06-26, Version 2.0.0-beta4
===============================

 * package: upgrade juggler to 2.0.0-beta2 (Miroslav Bajtoš)

 * Fix loopback in PhantomJS, fix karma tests (Miroslav Bajtoš)

 * Allow peer to use beta2 of datasource-juggler (and future) (Laurent)

 * Remove `app.boot` (Miroslav Bajtoš)

 * Update juggler dep (Raymond Feng)

 * Remove relationNameFor (Raymond Feng)

 * Fix a slowdown caused by mutation of an incoming accessToken option. (Samuel Reed)

 * Fix remote method definition in client-server example (Ritchie Martori)

 * package: the next version will be a minor version (Miroslav Bajtoš)

 * lib/registry: `getModel` throws, add `findModel` (Miroslav Bajtoš)

 * lib/application: Remove forgotten `loopback` ref (Miroslav Bajtoš)

 * Allow customization of ACL http status (Karl Mikkelsen)

 * Expose loopback as `app.loopback` (Miroslav Bajtoš)

 * Remove loopback-explorer from dev deps (Miroslav Bajtoš)

 * registry: export DataSource class (Miroslav Bajtoš)

 * registry: fix non-unique default dataSources (Miroslav Bajtoš)

 * lib/registry fix jsdoc comments (Miroslav Bajtoš)

 * test: add debug logs (Miroslav Bajtoš)

 * refactor: extract runtime and registry (Miroslav Bajtoš)

 * Remove assertIsModel and isDataSource (Miroslav Bajtoš)

 * Add createModelFromConfig and configureModel() (Miroslav Bajtoš)

 * Make app.get/app.set available in browser (Miroslav Bajtoš)

 * package: upgrade Mocha to 1.20 (Miroslav Bajtoš)

 * test: fix ACL integration tests (Miroslav Bajtoš)

 * JSDoc fixes (crandmck)

 * Add a test case (Raymond Feng)

 * Set the role id to be generated (Raymond Feng)

 * Add loopback.version back (Miroslav Bajtoš)

 * Tidy up app.model() to remove duplicate & recusrive call (Raymond Feng)

 * Register existing model to app.models during app.model() (Raymond Feng)

 * JSDoc cleanup (crandmck)

 * Bump version so that we can republish (Raymond Feng)

 * Bump version (Raymond Feng)

 * Use constructor to reference the model class (Raymond Feng)

 * Allow the creation of access token to be overriden (Raymond Feng)

 * Fixup JSDocs; note: updateOrCreate function alias pulled out on separate line for docs (crandmck)

 * lib/loopback: fix jsdoc comments (Miroslav Bajtoš)

 * Rename DataModel to PersistedModel (Miroslav Bajtoš)

 * Added middleware and API doc headings (crandmck)

 * Update JSDoc (crandmck)

 * Update docs.json (Rand McKinney)

 * Removed old .md files from API docs (Rand McKinney)

 * Delete api-model.md (Rand McKinney)

 * Delete api-datasource.md (Rand McKinney)

 * Delete api-geopoint.md (Rand McKinney)

 * Remove duplicate doc content (Rand McKinney)

 * Add note about unavailable args to remote hooks. (Rand McKinney)

 * Undo incorrect changes I made -- per Ritchie (Rand McKinney)

 * Update strong-remoting to 1.5 (Ritchie Martori)

 * Remove "user" as arg to beforeRemote(..) (Rand McKinney)

 * Exclude express-middleware from browser bundle (Miroslav Bajtoš)

 * !fixup only set ctx.accessType when sharedMethod is available (Ritchie Martori)

 * Refactor ACL to allow for `methodNames` / aliases (Ritchie Martori)

 * test: Remove forgotten call of `console.log()` (Miroslav Bajtoš)

 * Update README and the module diagram (Raymond Feng)

 * Clean up express middleware dependencies (Raymond Feng)

 * Update strong-remoting dep (Raymond Feng)

 * Rename express-wrapper to express-middleware (Raymond Feng)

 * Clean up the tests (Raymond Feng)

 * Upgrade to Express 4.x (Raymond Feng)

 * Deprecate app.boot, remove app.installMiddleware (Miroslav Bajtoš)


2014-05-28, Version 2.0.0-beta3
===============================

 * package.json: fix malformed json (Miroslav Bajtoš)

 * 2.0.0-beta2 (Ritchie Martori)

 * 2.0.0-beta1 (Ritchie Martori)

 * Add RC version (Ritchie Martori)

 * Depend on juggler@1.6.0 (Ritchie Martori)

 * !fixup Mark DAO methods as delegate (Ritchie Martori)

 * Ensure changes are created in sync (Ritchie Martori)

 * Remove un-rectify-able changes (Ritchie Martori)

 * Rework change conflict detection (Ritchie Martori)

 * - Use the RemoteObjects class to find remote objects instead of creating a cache  - Use the SharedClass class to build the remote connector  - Change default base model from Model to DataModel  - Fix DataModel errors not logging correct method names  - Use the strong-remoting 1.4 resolver API to resolve dynamic remote methods (relation api)  - Remove use of fn object for storing remoting meta data (Ritchie Martori)

 * In progress: rework remoting meta-data (Ritchie Martori)

 * Add test for conflicts where both deleted (Ritchie Martori)

 * Rework replication test (Ritchie Martori)

 * bump juggler version (Ritchie Martori)

 * Change#getModel(), Doc cleanup, Conflict event (Ritchie Martori)

 * Add error logging for missing data (Ritchie Martori)

 * Fix issues when using MongoDB for replication (Ritchie Martori)

 * !fixup Test cleanup (Ritchie Martori)

 * Move replication implementation to DataModel (Ritchie Martori)

 * All tests passing (Ritchie Martori)

 * !fixup use DataModel instead of Model for all data based models (Ritchie Martori)

 * fixup! unskip failing tests (Ritchie Martori)

 * !fixup RemoteConnector tests (Ritchie Martori)

 * Add missing test/model file (Ritchie Martori)

 * Refactor DataModel remoting (Ritchie Martori)

 * !fixup .replicate() argument handling (Ritchie Martori)

 * Fixes for e2e replication / remote connector tests (Ritchie Martori)

 * Add replication e2e tests (Ritchie Martori)

 * fixup! Assert model exists (Ritchie Martori)

 * fixup! rename Change.track => rectifyModelChanges (Ritchie Martori)

 * Add model tests (Ritchie Martori)

 * Add replication example (Ritchie Martori)

 * Add Checkpoint model and Model replication methods (Ritchie Martori)

 * Add Change model (Ritchie Martori)


2014-07-16, Version 1.10.0
==========================

 * Remove unused dep (Raymond Feng)

 * Bump version and update deps (Raymond Feng)

 * Upgrade to loopback-datasource-juggler@1.7.0 (Raymond Feng)

 * Refactor modelBuilder to registry and set up default model (Raymond Feng)

 * Add a test case for credentials/challenges (Raymond Feng)

 * Fix credentials/challenges types (Raymond Feng)

 * Update modules for examples (Raymond Feng)

 * Split out aliases for deleteById and destroyAll functions for jsdoc. (crandmck)

 * Remove unused deps (Raymond Feng)

 * Refactor email verification tests into a new group (Raymond Feng)

 * Fix the typo (Raymond Feng)

 * Add an option to honor emailVerified (Raymond Feng)

 * Update module list in README (Raymond Feng)

 * Refine the test cases for relation REST APIs (Raymond Feng)

 * test: add check of Model remote methods (Miroslav Bajtoš)

 * Adjust the REST mapping for add/remove (Raymond Feng)

 * Add a test case for hasMany through add/remove remoting (Raymond Feng)

 * Fix the typo and add Bearer token support (Raymond Feng)

 * Update README (Raymond Feng)

 * Fix misleading token middleware documentation (Aleksandr Tsertkov)

 * app: update `url` on `listening` event (Miroslav Bajtoš)


2014-06-27, Version 1.9.1
=========================

 * Fix "ReferenceError: loopback is not defined" in registry.memory(). (Guilherme Cirne)

 * Invalid Access Token return 401 (Karl Mikkelsen)


2014-06-25, Version 1.9.0
=========================

 * Bump version and update deps (Raymond Feng)

 * Update debug setting (Raymond Feng)

 * Mark `app.boot` as deprecated. (Miroslav Bajtoš)

 * Update link to doc (Rand McKinney)

 * Update juggler dep (Raymond Feng)

 * Remove relationNameFor (Raymond Feng)

 * Fix a slowdown caused by mutation of an incoming accessToken option. (Samuel Reed)

 * package: the next version will be a minor version (Miroslav Bajtoš)

 * lib/application: Remove forgotten `loopback` ref (Miroslav Bajtoš)

 * Allow customization of ACL http status (Karl Mikkelsen)

 * Expose loopback as `app.loopback` (Miroslav Bajtoš)

 * registry: export DataSource class (Miroslav Bajtoš)

 * registry: fix non-unique default dataSources (Miroslav Bajtoš)

 * lib/registry fix jsdoc comments (Miroslav Bajtoš)

 * test: add debug logs (Miroslav Bajtoš)

 * refactor: extract runtime and registry (Miroslav Bajtoš)

 * Remove assertIsModel and isDataSource (Miroslav Bajtoš)

 * Add createModelFromConfig and configureModel() (Miroslav Bajtoš)

 * Make app.get/app.set available in browser (Miroslav Bajtoš)

 * package: upgrade Mocha to 1.20 (Miroslav Bajtoš)

 * test: fix ACL integration tests (Miroslav Bajtoš)

 * JSDoc fixes (crandmck)

 * Add a test case (Raymond Feng)

 * Set the role id to be generated (Raymond Feng)

 * Tidy up app.model() to remove duplicate & recusrive call (Raymond Feng)

 * Register existing model to app.models during app.model() (Raymond Feng)

 * JSDoc cleanup (crandmck)

 * Bump version so that we can republish (Raymond Feng)


2014-06-09, Version 1.8.6
=========================

 * Bump version (Raymond Feng)

 * Use constructor to reference the model class (Raymond Feng)

 * Allow the creation of access token to be overriden (Raymond Feng)

 * Fixup JSDocs; note: updateOrCreate function alias pulled out on separate line for docs (crandmck)

 * Added middleware and API doc headings (crandmck)

 * Update JSDoc (crandmck)

 * Update docs.json (Rand McKinney)

 * Removed old .md files from API docs (Rand McKinney)

 * Delete api-model.md (Rand McKinney)

 * Delete api-datasource.md (Rand McKinney)

 * Delete api-geopoint.md (Rand McKinney)

 * Remove duplicate doc content (Rand McKinney)

 * Add note about unavailable args to remote hooks. (Rand McKinney)

 * Undo incorrect changes I made -- per Ritchie (Rand McKinney)

 * Update strong-remoting to 1.5 (Ritchie Martori)

 * Remove "user" as arg to beforeRemote(..) (Rand McKinney)

 * !fixup only set ctx.accessType when sharedMethod is available (Ritchie Martori)

 * Refactor ACL to allow for `methodNames` / aliases (Ritchie Martori)

 * Update README and the module diagram (Raymond Feng)

 * app: implement `connector()` and `connectors` (Miroslav Bajtoš)

 * Fix a typo in `app.boot`. (Samuel Reed)

 * Make app.datasources unique per app instance (Miroslav Bajtoš)


2014-05-27, Version 1.8.5
=========================

 * Bump version (Raymond Feng)

 * Add postgresql to the keywords (Raymond Feng)

 * updated package.json with SOAP and framework keywords (altsang)

 * updated package.json with keywords and updated description (Raymond Feng)


2014-05-27, Version 1.8.4
=========================

 * Add more keywords (Raymond Feng)

 * Bump version (Raymond Feng)

 * app: flatten model config (Miroslav Bajtoš)

 * Fix the test for mocha 1.19.0 (Raymond Feng)

 * Update dependencies (Raymond Feng)

 * Added more keywords (Rand McKinney)

 * Update README and the module diagram (Raymond Feng)

 * added "REST API" keyword (Rand McKinney)

 * added 'web' and 'framework' keywords (Rand McKinney)

 * Make image URL absolute for npmjs.org. (Rand McKinney)

 * Use common syntax for juggler dep (Ritchie Martori)

 * Modify `loopback.rest` to include `loopback.token` (Miroslav Bajtoš)

 * Relax validation object test (Ritchie Martori)

 * Make juggler version a bit more strict to avoid pulling in breaking changes (Ritchie Martori)

 * Change module diagram to local png (Rand McKinney)

 * Add LoopBack modules diagram (crandmck)

 * Update README.md (sumitha)

 * Update README.md (Al Tsang)

 * added github prefix to path (altsang)

 * removed githalytics, added sl-beacon (altsang)

 * Update README and license link (Raymond Feng)

 * Add CLA (Raymond Feng)


2014-05-16, Version 1.8.2
=========================

 * test/geo-point: relax too precise assertions (Miroslav Bajtoš)

 * Fix typo "Unkown" => "Unknown" (Adam Schwartz)

 * Support all 1.x versions of datasource-juggler (Miroslav Bajtoš)

 * Remove validation methods, now covered in JSDoc. (Rand McKinney)

 * Remove docs/api-geopoint.md from docs (Rand McKinney)

 * Removed docs/api-datasource.md (Rand McKinney)

 * Update README.md (Al Tsang)

 * Update README.md (Rand McKinney)

 * Move content from wiki on LB modules. (Rand McKinney)

 * Add homepage to package.json (Ritchie Martori)

 * Fix bug in User#resetPassword (haio)

 * Fix client-server example (Ritchie Martori)

 * Ensure roleId and principalId to be string in Role#isInRole (haio)

 * typo (haio)

 * Add more check on principalId (haio)

 * Convert principalId to String (haio)


2014-04-24, Version 1.8.1
=========================

 * Bump version (Raymond Feng)

 * Fix constructor JSDoc (crandmck)

 * Remove intermediate section headers from nav (crandmck)

 * Rename the method so that it won't conflict with Model.checkAccess (Raymond Feng)

 * Fix/remove ctx.user documentation (Ritchie Martori)

 * Documentation cleanup (Ritchie Martori)

 * Fix save implementation for remoting connector (Ritchie Martori)

 * Add basic Remote connector e2e test (Ritchie Martori)

 * Bump juggler version (Ritchie Martori)

 * Add test for remoting nested hidden properties (Ritchie Martori)

 * Fix #229 (Whitespaces removed (Alex Pica)

 * Add nodemailer to browser ignores (Ritchie Martori)

 * Add an assertion to the returned store object (Raymond Feng)

 * Add an integration test for belongsTo remoting (Raymond Feng)

 * Depend on strong-remoting 1.3 (Ritchie Martori)

 * Support host / port in Remote connector (Ritchie Martori)

 * Throw useful errors in DataModel stub methods (Ritchie Martori)

 * Move proxy creation from remote connector into base model class (Ritchie Martori)

 * Remove reload method body (Ritchie Martori)

 * Add Remote connector (Ritchie Martori)

 * Initial client-server example (Ritchie Martori)


2014-04-04, Version 1.7.4
=========================

 * Clean up JSDoc comments.  Remove doc for deprecated installMiddleware function (crandmck)

 * Describe the "id" parameter of model's sharedCtor (Miroslav Bajtoš)

 * Update and cleanup JSDoc (crandmck)

 * Cleanup and update of jsdoc (crandmck)

 * Add link to loopback.io (Rand McKinney)

 * Update user.js (Doug Toppin)

 * Add hidden property documentation (Ritchie Martori)

 * test: add hasAndBelongsToMany integration test (Miroslav Bajtoš)

 * fix to enable ACL for confirm link sent by email (Doug Toppin)

 * Add hidden property support to models (Ritchie Martori)

 * Allow app.model() to accept a DataSource instance (Ritchie Martori)

 * Make verifications url safe (Ritchie Martori)

 * Try to fix  org.pegdown.ParsingTimeoutException (Rand McKinney)

 * using base64 caused an occasional token string to contain '+' which resulted in a space being embedded in the token.  'hex' should always produce a url safe string for the token. (Doug Toppin)

 * Sending email was missing the from field (Doug Toppin)


2014-03-19, Version 1.7.2
=========================

 * Bump version (Raymond Feng)

 * Add more comments (Raymond Feng)

 * Improve the ACL matching algorithm (Raymond Feng)


2014-03-18, Version 1.7.1
=========================

 * Add test for request pausing during authentication (Miroslav Bajtoš)

 * Pause the req before checking access (Raymond Feng)

 * Remove the generated flag as the id is set by the before hook (Raymond Feng)

 * Improvements to JSDoc comments (crandmck)

 * Fixes to JSDoc for API docs (crandmck)

 * Remove oauth2 models as they will be packaged in a separate module (Raymond Feng)

 * Update api-model.md (Rand McKinney)

 * Minor doc fix (Ritchie Martori)

 * Set the correct status code for User.login (Raymond Feng)


2014-02-21, Version 1.7.0
=========================

 * Bump version to 1.7.0 (Raymond Feng)

 * Update deps (Raymond Feng)

 * Bump version and update deps (Raymond Feng)

 * Rewrite test for clear handler cache. (Guilherme Cirne)

 * Allows options to be passed to strong-remoting (Raymond Feng)

 * Remove coercion from port check (Ritchie Martori)

 * The simplest possible solution for clearing the handler cache when registering a model. (Guilherme Cirne)

 * Remove outdated test readme (Ritchie Martori)

 * Remove unnecessary lines (Alberto Leal)

 * Update the license text (Raymond Feng)

 * Make sure User/AccessToken relations are set up by default (Raymond Feng)

 * Remove unused karma packages (Ritchie Martori)

 * Add karma for running browser tests (Ritchie Martori)

 * Dual license: MIT + StrongLoop (Raymond Feng)


2014-02-12, Version 1.6.2
=========================

 * Bump version and update deps (Raymond Feng)

 * Documentation (generated) fix (Aurelien Chivot)

 * Use hex encoding for application ids/keys (Raymond Feng)

 * Add app.isAuthEnabled. (Miroslav Bajtoš)

 * Make app.models unique per app instance (Miroslav Bajtoš)

 * Fix incorrect usage of `app` in app.test.js (Miroslav Bajtoš)

 * Make sure the configured ACL submodel is used (Raymond Feng)


2014-01-30, Version 1.6.1
=========================

 * Add `include=user` param to `User.login` (Miroslav Bajtoš)

 * Describe `access_token` param of `User.logout` (Miroslav Bajtoš)

 * Remove the generated flag for access token id (Raymond Feng)

 * Remove message prefix as debug will print it (Raymond Feng)

 * Add debug information for user.login (Raymond Feng)


2014-01-27, Version 1.6.0
=========================

 * Update dependencies (Miroslav Bajtoš)

 * Add loopback.compat to simplify upgrade to 1.6 (Miroslav Bajtoš)

 * Register exported models using singular names (Miroslav Bajtoš)

 * User: use User.http.path (Miroslav Bajtoš)


2014-01-23, Version 1.5.3
=========================

 * Bump version (Raymond Feng)

 * Add a test for autoAttach (Raymond Feng)

 * Fix the Role ref to RoleMapping (Raymond Feng)

 * Fix the Scope reference to models (Raymond Feng)

 * Lookup the email model (Raymond Feng)

 * Add lookback.getModelByType() and use it resolve model deps (Raymond Feng)

 * Fix user test race condition (Ritchie Martori)

 * Fix race condition where MyEmail model was not attached to the correct dataSource in tests (Ritchie Martori)

 * Fix the method args (Raymond Feng)

 * Fix the typo for the method name (Raymond Feng)

 * Small change to text webhook. (Rand McKinney)

 * Minor wording change for testing purposes. (Rand McKinney)

 * Fix capitalization and punctuation. (Rand McKinney)

 * Minor wording cleanup. (Rand McKinney)

 * Prevent autoAttach from overriding existing data source (Raymond Feng)


2014-01-17, Version 1.5.2
=========================

 * Bump version (Raymond Feng)

 * Clean up loopback.js doc and add it to docs.json (Raymond Feng)

 * Fix the jsdoc for loopback.getModel() (Raymond Feng)

 * Make sure defaultPermission is checked (Raymond Feng)

 * Remove the dangling require (Raymond Feng)

 * Make ACL model subclassing friendly (Raymond Feng)

 * Fix heading levels in docs/ markdown files. (Miroslav Bajtoš)

 * Remove docs/rest.md (Miroslav Bajtoš)

 * Improve jsdox documentation of app object (Miroslav Bajtoš)


2014-01-14, Version 1.5.1
=========================

 * Bump version (Raymond Feng)

 * Make sure methods are called in the context of the calling class (Raymond Feng)

 * Start to move md to jsdoc (Ritchie Martori)


2014-01-14, Version 1.5.0
=========================

 * Replace `on` with `once` in middleware examples (Miroslav Bajtoš)

 * Fix incorrect transports (Ritchie Martori)

 * Speed up tests accessing User.password (Miroslav Bajtoš)

 * Describe loopback.ValidationError in API docs. (Miroslav Bajtoš)

 * Implement app.installMiddleware (Miroslav Bajtoš)

 * Implement `app.listen` (Miroslav Bajtoš)

 * Provide sane default for email connector transports (Ritchie Martori)

 * Add an empty transportsIndex to the mail connector by default (Ritchie Martori)

 * Add missing assert in user model (Ritchie Martori)

 * docs: document remote method `description` (Miroslav Bajtoš)


2014-01-07, Version 1.4.2
=========================

 * Bump version (Raymond Feng)

 * Add app.restApiRoot setting (Miroslav Bajtoš)

 * Fix links so they work on apidocs site. (Rand McKinney)

 * Add ValidationError to loopback exports. (Miroslav Bajtoš)

 * Add API docs to README (Ritchie Martori)

 * Fixed some broken links and added ACL example in createModel() (Rand McKinney)


2013-12-20, Version 1.4.1
=========================

 * Explicitly depend on juggler@1.2.11 (Ritchie Martori)

 * Add e2e tests for relations (Ritchie Martori)

 * Fix destroyAll reference (Ritchie Martori)

 * Add reference documentation using sdocs (Ritchie)

 * Update README for application model (Raymond Feng)


2013-12-18, Version 1.4.0
=========================

 * app.boot() now loads the "boot" directory (Ritchie Martori)

 * Clean up the test case (Raymond Feng)

 * Remove the default values for gateway/port (Raymond Feng)

 * Reformat the code using 2 space identation (Raymond Feng)

 * Allow cert/key data to be shared by push/feedback (Raymond Feng)

 * fixup - Include accessToken in user logout tests (Ritchie Martori)

 * Logout now automatically pulls the accessToken from the request (Ritchie Martori)

 * Fix tests depending on old behavior of default User ACLs (Ritchie Martori)

 * Add default user ACLs (Ritchie Martori)

 * Define schema for GCM push-notification settings (Miroslav Bajtoš)

 * Improve debug statements for access control (Ritchie Martori)


2013-12-13, Version 1.3.4
=========================

 * Dont attempt access checking on models without a check access method (Ritchie Martori)

 * App config settings are now available from app.get() (Ritchie Martori)

 * Fix user not allowed to delete itself if user (Ritchie Martori)

 * Only look at cookies if they are available (Ritchie Martori)

 * Remove the empty comment and set default token (Raymond Feng)

 * Refactor to the code use wrapper classes (Raymond Feng)

 * Enhance getRoles() to support smart roles (Raymond Feng)

 * Fix the algorithm for Role.isInRole and ACL.checkAccess (Raymond Feng)

 * Various ACL fixes (Ritchie Martori)

 * Add user default ACLs (Ritchie Martori)

 * Allow requests without auth tokens (Ritchie Martori)

 * Fix base class not being actual base class (Ritchie Martori)

 * Fix the ACL resolution against rules by matching score (Raymond Feng)

 * Add access type checking (Ritchie Martori)

 * Add Model.requireToken for disabling token requirement (Ritchie Martori)

 * Add Model.requireToken, default swagger to false (Ritchie Martori)

 * Add password reset (Ritchie Martori)


2013-12-06, Version 1.3.3
=========================

 * Bump version (Raymond Feng)


2013-12-06, Version show
========================

 * Bump version (Raymond Feng)

 * Make loopback-datasource-juggler a peer dep (Raymond Feng)

 * Add blank line before list so it lays out properly. (Rand McKinney)

 * Fix list format and minor wording fix. (Rand McKinney)

 * Small fix to link text (Rand McKinney)

 * Minor formatting and wording fixes. (Rand McKinney)

 * docs: describe http mapping of arguments (Miroslav Bajtos)

 * SLA-725 support PORT and HOST environment for PaaS support (Ritchie)


2013-12-04, Version 1.3.1
=========================

 * Fix the test assertion as the error message is changed. (Raymond Feng)

 * Bump version (Raymond Feng)

 * Remove superfluous head1 (Rand McKinney)

 * Fixed some list formatting issues. (Rand McKinney)

 * Fix list formats to play well with wiki markdown macro. (Rand McKinney)

 * Minor reformatting. (Rand McKinney)

 * Sadly, HTML table format is unusable in documentation wiki.  Revert to lame md format. (Rand McKinney)

 * Reformat table for /find operation arguments (Rand McKinney)

 * Deleted extra space that foiled bold formatting (Rand McKinney)

 * Changed h3's to bold text to avoid generating items in TOC (Rand McKinney)

 * Fixed erroneous heading level (Rand McKinney)

 * Use loopback.AccessToken as default (Ritchie Martori)

 * Fix missing assert (Ritchie Martori)

 * Minor formatting fixes to make it play well in wiki (Rand McKinney)

 * Initial auth implementation (Ritchie Martori)

 * added Google Analytics to README.md to test tracking (altsang)

 * Delete types.md (Rand McKinney)

 * Delete resources.md (Rand McKinney)

 * Delete quickstart.md (Rand McKinney)

 * Delete js.md (Rand McKinney)

 * Delete java.md (Rand McKinney)

 * Delete ios.md (Rand McKinney)

 * Delete gettingstarted.md (Rand McKinney)

 * Delete concepts.md (Rand McKinney)

 * Delete cli.md (Rand McKinney)

 * Delete bundled-models.md (Rand McKinney)

 * Delete apiexplorer.md (Rand McKinney)

 * Delete intro.md (Rand McKinney)

 * Add .jshintignore (Miroslav Bajtos)

 * Add test for findById returning 404 (Miroslav Bajtos)

 * Fix minor autoWiring bugs (Ritchie Martori)

 * Add unauthenticated role (Raymond Feng)

 * Add checkAccess for subject and token (Raymond Feng)

 * Start to support smart roles such as owner (Raymond Feng)

 * Add jshint configuration. (Miroslav Bajtos)

 * Update rest.md (Rand McKinney)

 * Update api.md (Rand McKinney)

 * Add status middleware (Ritchie Martori)

 * Auto attach all models created (Ritchie Martori)

 * Update docs.json (Rand McKinney)

 * Add loopback.urlNotFound() middleware. (Miroslav Bajtos)

 * Remove .attachTo() from tests (Ritchie Martori)

 * Create api-model-remote.md (Rand McKinney)

 * Create api-model.md (Rand McKinney)

 * Create api-geopoint.md (Rand McKinney)

 * Create api-datasource.md (Rand McKinney)

 * Create api-app.md (Rand McKinney)

 * Debugging odd defineFK behavior (Ritchie Martori)

 * Update the doc link (Raymond Feng)

 * Initial auto wiring for model dataSources (Ritchie Martori)

 * Add public flag checking (Ritchie Martori)


2013-11-18, Version 1.3.0
=========================

 * Upgrade nodemailer (Ritchie Martori)

 * Bump minor version (Ritchie Martori)

 * Add LoopBack forum link (Raymond Feng)

 * Remove blanket (Raymond Feng)

 * Switch to modelBuilder (Raymond Feng)

 * Allow ACLs for methods/relations (Raymond Feng)

 * Allows LDL level ACLs (Raymond Feng)

 * Update dependencies (Raymond Feng)

 * Fix the permission resolution (Raymond Feng)

 * Simplify check permission (Raymond Feng)

 * Fix the permission check (Raymond Feng)

 * Add oauth2 related models (Raymond Feng)

 * Add a stub to register role resolvers (Raymond Feng)

 * Add tests for isInRole and getRoles (Raymond Feng)

 * Add constants and more tests (Raymond Feng)

 * Define the models/relations for ACL (Raymond Feng)

 * Start to build the ACL models (Raymond Feng)

 * Update acl/role models (Raymond Feng)

 * Update ACL model (Raymond Feng)

 * Update AccessToken and User relationship (Ritchie Martori)

 * Added AccessToken created property (Ritchie Martori)

 * Update session / token documentation (Ritchie Martori)

 * Add loopback.token() middleware (Ritchie Martori)

 * Rename Session => AccessToken (Ritchie)

 * Bump verison (Ritchie Martori)

 * Fix bundle model name casing (Ritchie Martori)

 * Remove old node versions from travis (Ritchie Martori)

 * Add explicit Strong-remoting dep version (Ritchie Martori)

 * Add travis (Ritchie Martori)

 * Bump version (Ritchie Martori)

 * Update "hasMany" example (Ritchie Martori)

 * Code review fixes based on feedback from https://github.com/strongloop/loopback/pull/57 (Ritchie Martori)

 * Automatically convert strings to connectors if they are LoopBack connectors (Ritchie Martori)

 * Update api.md (Rand McKinney)

 * Update docs.json (Rand McKinney)

 * Create types.md (Rand McKinney)

 * Create bundled-models.md (Rand McKinney)

 * Update java.md (Rand McKinney)

 * Add app.dataSource() method (Ritchie)

 * Add app.boot() (Ritchie Martori)

 * README updates (Ritchie Martori)

 * Remove the proxy as it is now handled by the juggler (Raymond Feng)

 * Add MySQL connector (Raymond Feng)

 * Add belongsTo and hasAndBelongsToMany (Raymond Feng)

 * Clean up the model (Raymond Feng)

 * Added link to Doxygen API docs. (Rand McKinney)

 * Refactor email model into mail connector (Ritchie Martori)

 * Update Application model for the push notification (Raymond Feng)

 * Fix missing assert module (Ritchie Martori)

 * Fix the test as DAO now ignores undefined value for query (Raymond Feng)

 * Simplified LB architecture diagram (crandmck)

 * reorg and rewriting of first part of LoopBack guide, with new diagram (crandmck)

 * Fix the id and property access (Raymond Feng)

 * Update remote method example (Ritchie)

 * Update intro.md (Rand McKinney)

 * Update rest.md (Rand McKinney)

 * more cleanup (altsang)

 * remove >>>>>> from bad merge (altsang)

 * merged in Schoons' changes to mobile clients section (altsang)

 * revised per Ritchie's comments (altsang)

 * Revise Mobile Clients copy. (Michael Schoonmaker)

 * added Matt S' section on Mobile Clients (altsang)

 * filled out Big Picture (altsang)

 * revised shell -> node.js api (altsang)

 * Fix the preposition (Raymond Feng)

 * One more fix based on the comment (Raymond Feng)

 * Drop sure (Raymond Feng)

 * Add the missing article (Raymond Feng)

 * Add section for api explorer to docs (Raymond Feng)

 * added apiexplorer placeholder and big picture (altsang)

 * removed 'the' before StrongLoop Suite (altsang)

 * Remove redundant version in docs and testing docs webhook (Ritchie Martori)

 * removed version text (altsang)

 * Add keywords to package.json (Raymond Feng)

 * Add repo (Raymond Feng)

 * Finalize package.json for sls-1.0.0 (Raymond Feng)

 * Update docs for api->project rename. (Michael Schoonmaker)

 * Use a pure JS bcrypt (Ritchie)

 * Added little boxes to Getting Started. (Michael Schoonmaker)

 * Update assets mapping (Raymond Feng)

 * Update concepts doc with a new diagram (Raymond Feng)

 * Simplify readme (Ritchie Martori)

 * Add placeholders for client apis (Ritchie Martori)

 * Add command line docs (Ritchie Martori)

 * Add getting started link (Ritchie Martori)

 * Update the Quick Start (Ritchie Martori)

 * Fix package.json to remove duplicate mocha deps (Raymond Feng)

 * Tidy up package.json for LoopBack 1.0.0 (Raymond Feng)

 * Update model docs further. (Michael Schoonmaker)

 * Update license (Raymond Feng)

 * Updated model docs (Ritchie Martori)

 * Concepts overhaul in progress (Ritchie Martori)

 * Update the rest doc with more samples, fix the curl encoding (Raymond Feng)

 * Remove the todos example and fix doc example (Ritchie Martori)

 * doc/concepts: fixed link to strong-remoting docs (Miroslav Bajtos)

 * Update the internal prefix (Raymond Feng)

 * Update findOne (Raymond Feng)

 * Update REST doc based on the PR feedback (Raymond Feng)

 * Update REST doc (Raymond Feng)

 * Update the docs to fix into width of 80 (Raymond Feng)

 * intro edits and TOC adjustments (Al Tsang)

 * Fix the test case (Raymond Feng)


2013-08-27, Version 0.2.1
=========================

 * Doc edits (Ritchie Martori)

 * Update concepts (Raymond Feng)

 * Add more description about the filter arg for find() (Raymond Feng)

 * Update the concepts.md and link to related guides (Raymond Feng)

 * Update rest.md (Raymond Feng)

 * Add quickstart (Ritchie Martori)

 * Start to add rest.md (Raymond Feng)

 * adjusting concept headers, cleaning up intro, more instructions on getting started (Al Tsang)

 * Use findById to look up the instance by id (Raymond Feng)

 * Update the list of shared methods (Raymond Feng)

 * Make sure User.setup calls Model.setup to support shared ctor (Raymond Feng)

 * Add LICENSE (Raymond Feng)

 * Added code coverage blanket.js (cgole)

 * took google docs TOC and put into sdocs (Al Tsang)

 * Added placeholder docs (Ritchie Martori)

 * Use strong-task-emitter (Raymond Feng)

 * Rename 'loopback-data' to 'loopback-datasource-juggler' (Raymond Feng)

 * Fix login query (Ritchie Martori)

 * Implement required and update invlaid id schemas (Ritchie Martori)

 * Remove auth middleware and passport until adding in acl and strategies (Ritchie Martori)

 * Clean up log out methods (Ritchie Martori)

 * Swagger integration (Ritchie)

 * Fix hasMany / relational methods. Update docs. (Ritchie)

 * Add root true to remote methods (Ritchie)

 * Fix bad connector path (Ritchie)

 * Fix the test case (Raymond Feng)

 * Rename adapter to connector (Raymond Feng)

 * Add more docs and apis to application model (Raymond Feng)

 * Add a deleteById test (Raymond Feng)

 * Rename sl-remoting to strong-remoting (Ritchie Martori)

 * Add more functions and tests for Application model (Raymond Feng)

 * More readme cleanup (Ritchie)

 * README cleanup (Ritchie)

 * Fix renaming manually (Ritchie)

 * Manually merge application (Ritchie)

 * Manually merge rest adapter (Ritchie)

 * Add fields documentation (Ritchie)

 * More cleanup for test/README.md (Ritchie Martori)

 * Cleanup test markdown (Ritchie Martori)

 * Add memory docs and test (Ritchie Martori)

 * Remove remote option object (Ritchie Martori)

 * Rename jugglingdb to loopback-data (Raymond Feng)

 * Add renamed files (Raymond Feng)

 * rename asteroid to loopback (Raymond Feng)

 * Fix model remoting issue. (Ritchie Martori)

 * Fix inheritance bug (Ritchie Martori)

 * Remove updateAttribute as remote method (Ritchie Martori)

 * Fix login bug. (Ritchie Martori)

 * Added bcrypt for password hashing (Ritchie Martori)

 * Refactor Model into class. Make createModel() just sugar. (Ritchie Martori)

 * Remove data argument name from user tests (Ritchie Martori)

 * Validate uniqueness and format of User email. (Ritchie Martori)

 * Add user.logout() sugar method and update logout docs (Ritchie Martori)

 * Create 64 byte session ids (Ritchie Martori)

 * Tests README (Ritchie Martori)

 * Experiment application model (Raymond Feng)

 * Updated generated test docs (Ritchie Martori)

 * Update docs and add asteroid.memory() sugar api (Ritchie Martori)

 * Add exports to models (Raymond Feng)

 * Updating models (Raymond Feng)

 * Add basic email verification (Ritchie Martori)

 * Initial users (Ritchie Martori)

 * Add default user properties (Ritchie Martori)

 * Add initial User model (Ritchie Martori)

 * Remove app.modelBuilder() (Ritchie Martori)

 * Add more user model docs (Ritchie Martori)

 * Update README.md (cgole)

 * Fix type in docs (Ritchie Martori)

 * Add normalized properties to Models (Ritchie Martori)

 * Add schema skeletons for built-in models (Raymond Feng)

 * Fix service() & services() (Raymond Feng)

 * Add service method (Ritchie Martori)

 * Add more info to the models (Raymond Feng)

 * Add more information to the logical models (Raymond Feng)

 * Only build a sl remoting handler when a model is added to the app. (Ritchie Martori)

 * Add user model docs. (Ritchie Martori)

 * Bump version (Ritchie Martori)

 * Add geo point tests (Ritchie Martori)

 * Rename long to lng (Ritchie Martori)

 * Add geo point (Ritchie Martori)

 * model.find => model.findById, model.all => model.find (Ritchie Martori)


2013-06-24, Version 0.8.0
=========================

 * First release!


================================================
FILE: CODEOWNERS
================================================
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners,
# the last matching pattern has the most precendence.

# Current maintainers

* @bajtos @fabien @clarkorz @ebarault @zbarbuto @nitro404

# Alumni

_ @lehni


================================================
FILE: CONTRIBUTING.md
================================================
### Contributing ###

Thank you for your interest in `loopback`, an open source project
administered by StrongLoop.

Contributing to `loopback` is easy. In a few simple steps:

  * Ensure that your effort is aligned with the project's roadmap by
    talking to the maintainers, especially if you are going to spend a
    lot of time on it.

  * Make something better or fix a bug.

  * Adhere to code style outlined in the [Google C++ Style Guide][] and
    [Google Javascript Style Guide][].

  * Sign the [Contributor License Agreement](https://cla.strongloop.com/agreements/strongloop/loopback)

  * Submit a pull request through Github.


### Contributor License Agreement ###

```
  Individual Contributor License Agreement

  By signing this Individual Contributor License Agreement
  ("Agreement"), and making a Contribution (as defined below) to
  StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and
  agree to the following terms and conditions for Your present and
  future Contributions submitted to StrongLoop. Except for the license
  granted in this Agreement to StrongLoop and recipients of software
  distributed by StrongLoop, You reserve all right, title, and interest
  in and to Your Contributions.

  1. Definitions

     "You" or "Your" shall mean the copyright owner or the individual
     authorized by the copyright owner that is entering into this
     Agreement with StrongLoop.

     "Contribution" shall mean any original work of authorship,
     including any modifications or additions to an existing work, that
     is intentionally submitted by You to StrongLoop for inclusion in,
     or documentation of, any of the products owned or managed by
     StrongLoop ("Work"). For purposes of this definition, "submitted"
     means any form of electronic, verbal, or written communication
     sent to StrongLoop or its representatives, including but not
     limited to communication or electronic mailing lists, source code
     control systems, and issue tracking systems that are managed by,
     or on behalf of, StrongLoop for the purpose of discussing and
     improving the Work, but excluding communication that is
     conspicuously marked or otherwise designated in writing by You as
     "Not a Contribution."

  2. You Grant a Copyright License to StrongLoop

     Subject to the terms and conditions of this Agreement, You hereby
     grant to StrongLoop and recipients of software distributed by
     StrongLoop, a perpetual, worldwide, non-exclusive, no-charge,
     royalty-free, irrevocable copyright license to reproduce, prepare
     derivative works of, publicly display, publicly perform,
     sublicense, and distribute Your Contributions and such derivative
     works under any license and without any restrictions.

  3. You Grant a Patent License to StrongLoop

     Subject to the terms and conditions of this Agreement, You hereby
     grant to StrongLoop and to recipients of software distributed by
     StrongLoop a perpetual, worldwide, non-exclusive, no-charge,
     royalty-free, irrevocable (except as stated in this Section)
     patent license to make, have made, use, offer to sell, sell,
     import, and otherwise transfer the Work under any license and
     without any restrictions. The patent license You grant to
     StrongLoop under this Section applies only to those patent claims
     licensable by You that are necessarily infringed by Your
     Contributions(s) alone or by combination of Your Contributions(s)
     with the Work to which such Contribution(s) was submitted. If any
     entity institutes a patent litigation against You or any other
     entity (including a cross-claim or counterclaim in a lawsuit)
     alleging that Your Contribution, or the Work to which You have
     contributed, constitutes direct or contributory patent
     infringement, any patent licenses granted to that entity under
     this Agreement for that Contribution or Work shall terminate as
     of the date such litigation is filed.

  4. You Have the Right to Grant Licenses to StrongLoop

     You represent that You are legally entitled to grant the licenses
     in this Agreement.

     If Your employer(s) has rights to intellectual property that You
     create, You represent that You have received permission to make
     the Contributions on behalf of that employer, that Your employer
     has waived such rights for Your Contributions, or that Your
     employer has executed a separate Corporate Contributor License
     Agreement with StrongLoop.

  5. The Contributions Are Your Original Work

     You represent that each of Your Contributions are Your original
     works of authorship (see Section 8 (Submissions on Behalf of
     Others) for submission on behalf of others). You represent that to
     Your knowledge, no other person claims, or has the right to claim,
     any right in any intellectual property right related to Your
     Contributions.

     You also represent that You are not legally obligated, whether by
     entering into an agreement or otherwise, in any way that conflicts
     with the terms of this Agreement.

     You represent that Your Contribution submissions include complete
     details of any third-party license or other restriction (including,
     but not limited to, related patents and trademarks) of which You
     are personally aware and which are associated with any part of
     Your Contributions.

  6. You Don't Have an Obligation to Provide Support for Your Contributions

     You are not expected to provide support for Your Contributions,
     except to the extent You desire to provide support. You may provide
     support for free, for a fee, or not at all.

  6. No Warranties or Conditions

     StrongLoop acknowledges that unless required by applicable law or
     agreed to in writing, You provide Your Contributions on an "AS IS"
     BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
     EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
     OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR
     FITNESS FOR A PARTICULAR PURPOSE.

  7. Submission on Behalf of Others

     If You wish to submit work that is not Your original creation, You
     may submit it to StrongLoop separately from any Contribution,
     identifying the complete details of its source and of any license
     or other restriction (including, but not limited to, related
     patents, trademarks, and license agreements) of which You are
     personally aware, and conspicuously marking the work as
     "Submitted on Behalf of a Third-Party: [named here]".

  8. Agree to Notify of Change of Circumstances

     You agree to notify StrongLoop of any facts or circumstances of
     which You become aware that would make these representations
     inaccurate in any respect. Email us at callback@strongloop.com.
```

[Google C++ Style Guide]: https://google.github.io/styleguide/cppguide.html
[Google Javascript Style Guide]: https://google.github.io/styleguide/javascriptguide.xml


================================================
FILE: Gruntfile.js
================================================
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';

module.exports = function(grunt) {
  // Do not report warnings from unit-tests exercising deprecated paths
  process.env.NO_DEPRECATION = 'loopback';

  grunt.loadNpmTasks('grunt-mocha-test');

  // Project configuration.
  grunt.initConfig({
    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
    // Task configuration.
    uglify: {
      options: {
        banner: '<%= banner %>',
      },
      dist: {
        files: {
          'dist/loopback.min.js': ['dist/loopback.js'],
        },
      },
    },
    eslint: {
      gruntfile: {
        src: 'Gruntfile.js',
      },
      lib: {
        src: ['lib/**/*.js'],
      },
      common: {
        src: ['common/**/*.js'],
      },
      server: {
        src: ['server/**/*.js'],
      },
      test: {
        src: ['test/**/*.js'],
      },
    },
    watch: {
      gruntfile: {
        files: '<%= eslint.gruntfile.src %>',
        tasks: ['eslint:gruntfile'],
      },
      browser: {
        files: ['<%= eslint.browser.src %>'],
        tasks: ['eslint:browser'],
      },
      common: {
        files: ['<%= eslint.common.src %>'],
        tasks: ['eslint:common'],
      },
      lib: {
        files: ['<%= eslint.lib.src %>'],
        tasks: ['eslint:lib'],
      },
      server: {
        files: ['<%= eslint.server.src %>'],
        tasks: ['eslint:server'],
      },
      test: {
        files: ['<%= eslint.test.src %>'],
        tasks: ['eslint:test'],
      },
    },
    browserify: {
      dist: {
        files: {
          'dist/loopback.js': ['index.js'],
        },
        options: {
          ignore: ['nodemailer', 'passport', 'bcrypt'],
          standalone: 'loopback',
        },
      },
    },
    mochaTest: {
      'unit': {
        src: 'test/*.js',
        options: {
          reporter: 'dot',
          require: require.resolve('./test/helpers/use-english.js'),
        },
      },
      'unit-xml': {
        src: 'test/*.js',
        options: {
          reporter: 'xunit',
          captureFile: 'xunit.xml',
        },
      },
    },
    karma: {
      'unit-once': {
        configFile: 'test/karma.conf.js',
        browsers: ['ChromeDocker'],
        singleRun: true,
        reporters: ['dots', 'junit'],

        // increase the timeout for slow build slaves (e.g. Travis-ci)
        browserNoActivityTimeout: 30000,

        // CI friendly test output
        junitReporter: {
          outputFile: 'karma-xunit.xml',
        },

        browserify: {
          // Disable sourcemaps to prevent
          // Fatal error: Maximum call stack size exceeded
          debug: false,
          // Disable watcher, grunt will exit after the first run
          watch: false,
        },
      },
      unit: {
        configFile: 'test/karma.conf.js',
      },
      e2e: {
        options: {
          // base path, that will be used to resolve files and exclude
          basePath: '',

          // frameworks to use
          frameworks: ['mocha', 'browserify'],

          // list of files / patterns to load in the browser
          files: [
            'test/e2e/remote-connector.e2e.js',
            'test/e2e/replication.e2e.js',
          ],

          // list of files to exclude
          exclude: [

          ],

          // test results reporter to use
          // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
          reporters: ['dots'],

          // web server port
          port: 9876,

          // cli runner port
          runnerPort: 9100,

          // enable / disable colors in the output (reporters and logs)
          colors: true,

          // level of logging
          // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
          logLevel: 'warn',

          // enable / disable watching file and executing tests whenever any file changes
          autoWatch: true,

          // Start these browsers, currently available:
          // - Chrome
          // - ChromeCanary
          // - Firefox
          // - Opera
          // - Safari (only Mac)
          // - PhantomJS
          // - IE (only Windows)
          browsers: [
            'Chrome',
          ],

          // If browser does not capture in given timeout [ms], kill it
          captureTimeout: 60000,

          // Continuous Integration mode
          // if true, it capture browsers, run tests and exit
          singleRun: false,

          // Browserify config (all optional)
          browserify: {
            // extensions: ['.coffee'],
            ignore: [
              'nodemailer',
              'passport',
              'passport-local',
              'superagent',
              'supertest',
              'bcrypt',
            ],
            // transform: ['coffeeify'],
            // debug: true,
            // noParse: ['jquery'],
            watch: true,
          },

          // Add browserify to preprocessors
          preprocessors: {'test/e2e/*': ['browserify']},
        },
      },
    },

  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-eslint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-karma');

  grunt.registerTask('e2e-server', function() {
    const done = this.async();
    const app = require('./test/fixtures/e2e/app');
    app.listen(0, function() {
      process.env.PORT = this.address().port;
      done();
    });
  });

  grunt.registerTask('skip-karma', function() {
    console.log(`*** SKIPPING PHANTOM-JS BASED TESTS ON ${process.platform}` +
      ` ${process.arch} ***`);
  });

  grunt.registerTask('e2e', ['e2e-server', 'karma:e2e']);

  // Default task.
  grunt.registerTask('default', ['browserify']);

  grunt.registerTask('test', [
    'eslint',
    process.env.JENKINS_HOME ? 'mochaTest:unit-xml' : 'mochaTest:unit',
    process.env.JENKINS_HOME && (/^win/.test(process.platform) ||
      /^s390x/.test(process.arch) || /^ppc64/.test(process.arch)) ?
      'skip-karma' : 'karma:unit-once',
  ]);

  // alias for sl-ci-run and `npm test`
  grunt.registerTask('mocha-and-karma', ['test']);
};


================================================
FILE: LICENSE
================================================
Copyright (c) IBM Corp. 2013,2018. All Rights Reserved.
Node module: loopback
This project is licensed under the MIT License, full text below.

--------

MIT license

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.


================================================
FILE: README.md
================================================
# LoopBack

[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/strongloop/loopback?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Module LTS Adopted'](https://img.shields.io/badge/Module%20LTS-Adopted-brightgreen.svg?style=flat)](http://github.com/CloudNativeJS/ModuleLTS)
[![IBM Support](https://img.shields.io/badge/IBM%20Support-Frameworks-brightgreen.svg?style=flat)](http://ibm.biz/node-support)

**⚠️ LoopBack 3 has reached end of life. We are no longer accepting pull requests or providing 
support for community users. The only exception is fixes for critical bugs and security 
vulnerabilities provided as part of support for IBM API Connect customers.
We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as soon as possible. 
Learn more about
<a href="https://loopback.io/doc/en/contrib/Long-term-support.html">LoopBack's long term support policy.</a>
will be provided or accepted. (See
[Module Long Term Support Policy](#module-long-term-support-policy) below.)**

We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as
soon as possible. Refer to our
[Migration Guide](https://loopback.io/doc/en/lb4/migration-overview.html)
for more information on how to upgrade.

## Overview

LoopBack is a highly-extensible, open-source Node.js framework that enables you to:

  * Create dynamic end-to-end REST APIs with little or no coding.
  * Access data from Oracle, MySQL, PostgreSQL, MS SQL Server, MongoDB, SOAP and other REST APIs.
  * Incorporate model relationships and access controls for complex APIs.
  * Use built-in push, geolocation, and file services for mobile apps.
  * Easily create client apps using Android, iOS, and JavaScript SDKs.
  * Run your application on-premises or in the cloud.

LoopBack consists of:

  * A library of Node.js modules.
  * [Yeoman](http://yeoman.io/) generators for scaffolding applications.
  * Client SDKs for iOS, Android, and web clients.

LoopBack tools include:
  * Command-line tool `loopback-cli` to create applications, models, data sources, and so on.

For more details, see [https://loopback.io/](https://loopback.io/).


## Module Long Term Support Policy

LoopBack 3.x has reached End-of-Life.

This module adopts the [Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy, with the following End Of Life (EOL) dates:

| Version    | Status          | Published | EOL                  |
| ---------- | --------------- | --------- | -------------------- |
| LoopBack 4 | Current         | Oct 2018  | Apr 2023 _(minimum)_ |
| LoopBack 3 | End-of-Life     | Dec 2016  | Dec 2020             |
| LoopBack 2 | End-of-Life     | Jul 2014  | Apr 2019             |

Learn more about our LTS plan in [docs](https://loopback.io/doc/en/contrib/Long-term-support.html).

## LoopBack modules

The LoopBack framework is a set of Node.js modules that you can use independently or together.

![LoopBack modules](https://github.com/strongloop/loopback/raw/master/docs/assets/lb-modules.png "LoopBack modules")

### Core
* [loopback](https://github.com/strongloop/loopback)
* [loopback-datasource-juggler](https://github.com/strongloop/loopback-datasource-juggler)
* [strong-remoting](https://github.com/strongloop/strong-remoting)

### Connectors
* [loopback-connector-mongodb](https://github.com/strongloop/loopback-connector-mongodb)
* [loopback-connector-mysql](https://github.com/strongloop/loopback-connector-mysql)
* [loopback-connector-postgresql](https://github.com/strongloop/loopback-connector-postgresql)
* [loopback-connector-rest](https://github.com/strongloop/loopback-connector-rest)

### Enterprise Connectors
* [loopback-connector-oracle](https://github.com/strongloop/loopback-connector-oracle)
* [loopback-connector-mssql](https://github.com/strongloop/loopback-connector-mssql)
* [loopback-connector-soap](https://github.com/strongloop/loopback-connector-soap)
* [loopback-connector-atg](https://github.com/strongloop/loopback-connector-atg)

### Community Connectors

The LoopBack community has created and supports a number of additional connectors.  See [Community connectors](https://loopback.io/doc/en/lb2/Community-connectors.html) for details.

### Components
* [loopback-component-push](https://github.com/strongloop/loopback-component-push)
* [loopback-component-storage](https://github.com/strongloop/loopback-component-storage)
* [loopback-component-passport](https://github.com/strongloop/loopback-component-passport)

### Client SDKs
* [loopback-sdk-ios](https://github.com/strongloop/loopback-sdk-ios)
* [loopback-sdk-android](https://github.com/strongloop/loopback-sdk-android)
* [loopback-sdk-angular](https://github.com/strongloop/loopback-sdk-angular)
  * [loopback-sdk-angular-cli](https://github.com/strongloop/loopback-sdk-angular-cli)
  * [grunt-loopback-sdk-angular](https://github.com/strongloop/grunt-loopback-sdk-angular)

### Tools
* [loopback-explorer](https://github.com/strongloop/loopback-explorer)
* [loopback-workspace](https://github.com/strongloop/loopback-workspace)
* [generator-loopback](https://github.com/strongloop/generator-loopback)

### Examples

StrongLoop provides a number of example applications that illustrate various key LoopBack features. In some cases, they have accompanying step-by-step instructions (tutorials).

See [examples at loopback.io](https://loopback.io/examples/) for details.

## Resources

  * [Documentation](https://loopback.io/doc/).
  * [API documentation](https://apidocs.strongloop.com/loopback).
  * [LoopBack Announcements](https://groups.google.com/forum/#!forum/loopbackjs-announcements)
  * [LoopBack Google Group](https://groups.google.com/forum/#!forum/loopbackjs).
  * [GitHub issues](https://github.com/strongloop/loopback/issues).
  * [Gitter chat](https://gitter.im/strongloop/loopback).

## Contributing

Contributions to the LoopBack project are welcome! See [Contributing to LoopBack](https://loopback.io/doc/en/contrib/index.html) for more information.

## Reporting issues

One of the easiest ways to contribute to LoopBack is to report an issue. See [Reporting issues](https://loopback.io/doc/en/contrib/Reporting-issues.html) for more information.

[![Analytics](https://sl-beacon.appspot.com/UA-37775386-1/github/loopback/readme?pixel)](https://github.com/strongloop/loopback)


================================================
FILE: common/models/README.md
================================================
# Application

Application model represents the metadata for a client application that has its
own identity and associated configuration with the LoopBack server.

## Each application has the following basic properties:

* id: Automatically generated id
* name: Name of the application (required)
* description: Description of the application (optional)
* icon: URL of the icon
* status: Status of the application, such as production/sandbox/disabled
* created: Timestamp of the record being created
* modified: Timestamp of the record being modified

## An application has the following properties linking to users:

* owner: The user id of the developer who registers the application
* collaborators: A array of users ids who have permissions to work on this app

## oAuth 2.0 settings

* url: The application url
* callbackUrls: An array of preregistered callback urls for oAuth 2.0
* permissions: An array of oAuth 2.0 scopes that can be requested by the application

## Security keys

The following keys are automatically generated by the application creation
process. They can be reset upon request.

* clientKey: Secret for mobile clients
* javaScriptKey: Secret for JavaScript clients
* restApiKey: Secret for REST APIs
* windowsKey: Secret for Windows applications
* masterKey: Secret for REST APIS. It bypasses model level permissions

## Push notification settings

The application can be configured to support multiple methods of push notifications.

* pushSettings


     pushSettings: {
        apns: {
          certData: config.apnsCertData,
          keyData: config.apnsKeyData,
          production: false, // Development mode
          pushOptions: {
            // Extra options can go here for APN
          },
          feedbackOptions: {
            batchFeedback: true,
            interval: 300
          }
        },
        gcm: {
          serverApiKey: config.gcmServerApiKey
        }
      }


## Authentication schemes

* authenticationEnabled
* anonymousAllowed
* authenticationSchemes

### Authentication scheme settings

* scheme: Name of the authentication scheme, such as local, facebook, google,
twitter, linkedin, github
* credential: Scheme-specific credentials

## APIs for Application model

In addition to the CRUD methods, the Application model also has the following
apis:

### Register a new application

You can register a new application by providing the owner user id, application
name, and other properties in the options object.

    Application.register('rfeng', 'MyApp1',
        {description: 'My first loopback application'},
        function (err, result) {
            var app = result;
        ...
    });

### Reset keys

You can reset keys for a given application by id.

    Application.resetKeys(appId, function (err, result) {
        var app = result;
        ...
    });

### Authenticate by appId and key

You can authenticate an application by id and one of the keys. If successful,
it calls back with the key name in the result argument. Otherwise, the
keyName is null.

    Application.authenticate(appId, clientKey, function (err, keyName) {
            assert.equal(keyName, 'clientKey');
            ...
    });




================================================
FILE: common/models/access-token.js
================================================
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

/*!
 * Module Dependencies.
 */

'use strict';
const g = require('../../lib/globalize');
const loopback = require('../../lib/loopback');
const assert = require('assert');
const uid = require('uid2');
const DEFAULT_TOKEN_LEN = 64;

/**
 * Token based authentication and access control.
 *
 * **Default ACLs**
 *
 *  - DENY EVERYONE `*`
 *  - ALLOW EVERYONE create
 *
 * @property {String} id Generated token ID.
 * @property {Number} ttl Time to live in seconds, 2 weeks by default.
 * @property {Date} created When the token was created.
 * @property {Object} settings Extends the `Model.settings` object.
 * @property {Number} settings.accessTokenIdLength Length of the base64-encoded string access token. Default value is 64.
 * Increase the length for a more secure access token.
 *
 * @class AccessToken
 * @inherits {PersistedModel}
 */

module.exports = function(AccessToken) {
  /**
   * Anonymous Token
   *
   * ```js
   * assert(AccessToken.ANONYMOUS.id === '$anonymous');
   * ```
   */

  AccessToken.ANONYMOUS = new AccessToken({id: '$anonymous'});

  /**
   * Create a cryptographically random access token id.
   *
   * @callback {Function} callback
   * @param {Error} err
   * @param {String} token
   */

  AccessToken.createAccessTokenId = function(fn) {
    uid(this.settings.accessTokenIdLength || DEFAULT_TOKEN_LEN, function(err, guid) {
      if (err) {
        fn(err);
      } else {
        fn(null, guid);
      }
    });
  };

  /*!
   * Hook to create accessToken id.
   */
  AccessToken.observe('before save', function(ctx, next) {
    if (!ctx.instance || ctx.instance.id) {
      // We are running a partial update or the instance already has an id
      return next();
    }

    AccessToken.createAccessTokenId(function(err, id) {
      if (err) return next(err);
      ctx.instance.id = id;
      next();
    });
  });

  /**
   * Extract the access token id from the HTTP request
   * @param {Request} req HTTP request object
   * @options {Object} [options] Each option array is used to add additional keys to find an `accessToken` for a `request`.
   * @property {Array} [cookies] Array of cookie names.
   * @property {Array} [headers] Array of header names.
   * @property {Array} [params] Array of param names.
   * @property {Boolean} [searchDefaultTokenKeys] Use the default search locations for Token in request
   * @property {Boolean} [bearerTokenBase64Encoded] Defaults to `true`. For `Bearer` token based `Authorization` headers,
   * decode the value from `Base64`. If set to `false`, the decoding will be skipped and the token id will be the raw value
   * parsed from the header.
   * @return {String} The access token
   */
  AccessToken.getIdForRequest = function(req, options) {
    options = options || {};
    let params = options.params || [];
    let headers = options.headers || [];
    let cookies = options.cookies || [];
    let i = 0;
    let length, id;

    // https://github.com/strongloop/loopback/issues/1326
    if (options.searchDefaultTokenKeys !== false) {
      params = params.concat(['access_token']);
      headers = headers.concat(['X-Access-Token', 'authorization']);
      cookies = cookies.concat(['access_token', 'authorization']);
    }

    for (length = params.length; i < length; i++) {
      const param = params[i];
      // replacement for deprecated req.param()
      id = req.params && req.params[param] !== undefined ? req.params[param] :
        req.body && req.body[param] !== undefined ? req.body[param] :
          req.query && req.query[param] !== undefined ? req.query[param] :
            undefined;

      if (typeof id === 'string') {
        return id;
      }
    }

    for (i = 0, length = headers.length; i < length; i++) {
      id = req.header(headers[i]);

      if (typeof id === 'string') {
        // Add support for oAuth 2.0 bearer token
        // http://tools.ietf.org/html/rfc6750

        // To prevent Error: Model::findById requires the id argument
        // with loopback-datasource-juggler 2.56.0+
        if (id === '') continue;

        if (id.indexOf('Bearer ') === 0) {
          id = id.substring(7);
          if (options.bearerTokenBase64Encoded) {
            // Decode from base64
            const buf = new Buffer(id, 'base64');
            id = buf.toString('utf8');
          }
        } else if (/^Basic /i.test(id)) {
          id = id.substring(6);
          id = (new Buffer(id, 'base64')).toString('utf8');
          // The spec says the string is user:pass, so if we see both parts
          // we will assume the longer of the two is the token, so we will
          // extract "a2b2c3" from:
          //   "a2b2c3"
          //   "a2b2c3:"   (curl http://a2b2c3@localhost:3000/)
          //   "token:a2b2c3" (curl http://token:a2b2c3@localhost:3000/)
          //   ":a2b2c3"
          const parts = /^([^:]*):(.*)$/.exec(id);
          if (parts) {
            id = parts[2].length > parts[1].length ? parts[2] : parts[1];
          }
        }
        return id;
      }
    }

    if (req.signedCookies) {
      for (i = 0, length = cookies.length; i < length; i++) {
        id = req.signedCookies[cookies[i]];

        if (typeof id === 'string') {
          return id;
        }
      }
    }
    return null;
  };

  /**
   * Resolve and validate the access token by id
   * @param {String} id Access token
   * @callback {Function} cb Callback function
   * @param {Error} err Error information
   * @param {Object} Resolved access token object
   */
  AccessToken.resolve = function(id, cb) {
    this.findById(id, function(err, token) {
      if (err) {
        cb(err);
      } else if (token) {
        token.validate(function(err, isValid) {
          if (err) {
            cb(err);
          } else if (isValid) {
            cb(null, token);
          } else {
            const e = new Error(g.f('Invalid Access Token'));
            e.status = e.statusCode = 401;
            e.code = 'INVALID_TOKEN';
            cb(e);
          }
        });
      } else {
        cb();
      }
    });
  };

  /**
   * Find a token for the given `ServerRequest`.
   *
   * @param {ServerRequest} req
   * @param {Object} [options] Options for finding the token
   * @callback {Function} callback
   * @param {Error} err
   * @param {AccessToken} token
   */
  AccessToken.findForRequest = function(req, options, cb) {
    if (cb === undefined && typeof options === 'function') {
      cb = options;
      options = {};
    }

    const id = this.getIdForRequest(req, options);

    if (id) {
      this.resolve(id, cb);
    } else {
      process.nextTick(cb);
    }
  };

  /**
   * Validate the token.
   *
   * @callback {Function} callback
   * @param {Error} err
   * @param {Boolean} isValid
   */
  AccessToken.prototype.validate = function(cb) {
    try {
      assert(
        this.created && typeof this.created.getTime === 'function',
        'token.created must be a valid Date',
      );
      assert(this.ttl !== 0, 'token.ttl must be not be 0');
      assert(this.ttl, 'token.ttl must exist');
      assert(this.ttl >= -1, 'token.ttl must be >= -1');

      const AccessToken = this.constructor;
      const userRelation = AccessToken.relations.user; // may not be set up
      let User = userRelation && userRelation.modelTo;

      // redefine user model if accessToken's principalType is available
      if (this.principalType) {
        User = AccessToken.registry.findModel(this.principalType);
        if (!User) {
          process.nextTick(function() {
            return cb(null, false);
          });
        }
      }

      const now = Date.now();
      const created = this.created.getTime();
      const elapsedSeconds = (now - created) / 1000;
      const secondsToLive = this.ttl;
      const eternalTokensAllowed = !!(User && User.settings.allowEternalTokens);
      const isEternalToken = secondsToLive === -1;
      const isValid = isEternalToken ?
        eternalTokensAllowed :
        elapsedSeconds < secondsToLive;

      if (isValid) {
        process.nextTick(function() {
          cb(null, isValid);
        });
      } else {
        this.destroy(function(err) {
          cb(err, isValid);
        });
      }
    } catch (e) {
      process.nextTick(function() {
        cb(e);
      });
    }
  };
};


================================================
FILE: common/models/access-token.json
================================================
{
  "name": "AccessToken",
  "properties": {
    "id": {
      "type": "string",
      "id": true
    },
    "ttl": {
      "type": "number",
      "ttl": true,
      "default": 1209600,
      "description": "time to live in seconds (2 weeks by default)"
    },
    "scopes": {
      "type": ["string"],
      "description": "Array of scopes granted to this access token."
    },
    "created": {
      "type": "Date",
      "defaultFn": "now"
    }
  },
  "relations": {
    "user": {
      "type": "belongsTo",
      "model": "User",
      "foreignKey": "userId"
    }
  },
  "acls": [
    {
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    }
  ]
}


================================================
FILE: common/models/acl.js
================================================
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';
/*!
 Schema ACL options
 Object level permissions, for example, an album owned by a user
 Factors to be authorized against:
 * model name: Album
 * model instance properties: userId of the album, friends, shared
 * methods
 * app and/or user ids/roles
 ** loggedIn
 ** roles
 ** userId
 ** appId
 ** none
 ** everyone
 ** relations: owner/friend/granted
 Class level permissions, for example, Album
 * model name: Album
 * methods
 URL/Route level permissions
 * url pattern
 * application id
 * ip addresses
 * http headers
 Map to oAuth 2.0 scopes
 */

const g = require('../../lib/globalize');
const loopback = require('../../lib/loopback');
const utils = require('../../lib/utils');
const async = require('async');
const extend = require('util')._extend;
const assert = require('assert');
const debug = require('debug')('loopback:security:acl');

const ctx = require('../../lib/access-context');
const AccessContext = ctx.AccessContext;
const Principal = ctx.Principal;
const AccessRequest = ctx.AccessRequest;

const Role = loopback.Role;
assert(Role, 'Role model must be defined before ACL model');

/**
 * A Model for access control meta data.
 *
 * System grants permissions to principals (users/applications, can be grouped
 * into roles).
 *
 * Protected resource: the model data and operations
 * (model/property/method/relation/…)
 *
 * For a given principal, such as client application and/or user, is it allowed
 * to access (read/write/execute)
 * the protected resource?
 *
 * @header ACL
 * @property {String} model Name of the model.
 * @property {String} property Name of the property, method, scope, or relation.
 * @property {String} accessType Type of access being granted: one of READ, WRITE, or EXECUTE.
 * @property {String} permission Type of permission granted. One of:
 *
 *  - ALARM: Generate an alarm, in a system-dependent way, the access specified in the permissions component of the ACL entry.
 *  - ALLOW: Explicitly grants access to the resource.
 *  - AUDIT: Log, in a system-dependent way, the access specified in the permissions component of the ACL entry.
 *  - DENY: Explicitly denies access to the resource.
 * @property {String} principalType Type of the principal; one of: APPLICATION, USER, ROLE.
 * @property {String} principalId ID of the principal - such as appId, userId or roleId.
 * @property {Object} settings Extends the `Model.settings` object.
 * @property {String} settings.defaultPermission Default permission setting: ALLOW, DENY, ALARM, or AUDIT. Default is ALLOW.
 * Set to DENY to prohibit all API access by default.
 *
 * @class ACL
 * @inherits PersistedModel
 */

module.exports = function(ACL) {
  ACL.ALL = AccessContext.ALL;

  ACL.DEFAULT = AccessContext.DEFAULT; // Not specified
  ACL.ALLOW = AccessContext.ALLOW; // Allow
  ACL.ALARM = AccessContext.ALARM; // Warn - send an alarm
  ACL.AUDIT = AccessContext.AUDIT; // Audit - record the access
  ACL.DENY = AccessContext.DENY; // Deny

  ACL.READ = AccessContext.READ; // Read operation
  ACL.REPLICATE = AccessContext.REPLICATE; // Replicate (pull) changes
  ACL.WRITE = AccessContext.WRITE; // Write operation
  ACL.EXECUTE = AccessContext.EXECUTE; // Execute operation

  ACL.USER = Principal.USER;
  ACL.APP = ACL.APPLICATION = Principal.APPLICATION;
  ACL.ROLE = Principal.ROLE;
  ACL.SCOPE = Principal.SCOPE;

  ACL.DEFAULT_SCOPE = ctx.DEFAULT_SCOPES[0];

  /**
   * Calculate the matching score for the given rule and request
   * @param {ACL} rule The ACL entry
   * @param {AccessRequest} req The request
   * @returns {Number}
   */
  ACL.getMatchingScore = function getMatchingScore(rule, req) {
    const props = ['model', 'property', 'accessType'];
    let score = 0;

    for (let i = 0; i < props.length; i++) {
      // Shift the score by 4 for each of the properties as the weight
      score = score * 4;
      const ruleValue = rule[props[i]] || ACL.ALL;
      const requestedValue = req[props[i]] || ACL.ALL;
      const isMatchingMethodName = props[i] === 'property' &&
        req.methodNames.indexOf(ruleValue) !== -1;

      let isMatchingAccessType = ruleValue === requestedValue;
      if (props[i] === 'accessType' && !isMatchingAccessType) {
        switch (ruleValue) {
          case ACL.EXECUTE:
            // EXECUTE should match READ, REPLICATE and WRITE
            isMatchingAccessType = true;
            break;
          case ACL.WRITE:
            // WRITE should match REPLICATE too
            isMatchingAccessType = requestedValue === ACL.REPLICATE;
            break;
        }
      }

      if (isMatchingMethodName || isMatchingAccessType) {
        // Exact match
        score += 3;
      } else if (ruleValue === ACL.ALL) {
        // Wildcard match
        score += 2;
      } else if (requestedValue === ACL.ALL) {
        score += 1;
      } else {
        // Doesn't match at all
        return -1;
      }
    }

    // Weigh against the principal type into 4 levels
    // - user level (explicitly allow/deny a given user)
    // - app level (explicitly allow/deny a given app)
    // - role level (role based authorization)
    // - other
    // user > app > role > ...
    score = score * 4;
    switch (rule.principalType) {
      case ACL.USER:
        score += 4;
        break;
      case ACL.APP:
        score += 3;
        break;
      case ACL.ROLE:
        score += 2;
        break;
      default:
        score += 1;
    }

    // Weigh against the roles
    // everyone < authenticated/unauthenticated < related < owner < ...
    score = score * 8;
    if (rule.principalType === ACL.ROLE) {
      switch (rule.principalId) {
        case Role.OWNER:
          score += 4;
          break;
        case Role.RELATED:
          score += 3;
          break;
        case Role.AUTHENTICATED:
        case Role.UNAUTHENTICATED:
          score += 2;
          break;
        case Role.EVERYONE:
          score += 1;
          break;
        default:
          score += 5;
      }
    }
    score = score * 4;
    score += AccessContext.permissionOrder[rule.permission || ACL.ALLOW] - 1;
    return score;
  };

  /**
   * Get matching score for the given `AccessRequest`.
   * @param {AccessRequest} req The request
   * @returns {Number} score
   */

  ACL.prototype.score = function(req) {
    return this.constructor.getMatchingScore(this, req);
  };

  /*!
   * Resolve permission from the ACLs
   * @param {Object[]) acls The list of ACLs
   * @param {AccessRequest} req The access request
   * @returns {AccessRequest} result The resolved access request
   */
  ACL.resolvePermission = function resolvePermission(acls, req) {
    if (!(req instanceof AccessRequest)) {
      req.registry = this.registry;
      req = new AccessRequest(req);
    }
    // Sort by the matching score in descending order
    acls = acls.sort(function(rule1, rule2) {
      return ACL.getMatchingScore(rule2, req) - ACL.getMatchingScore(rule1, req);
    });
    let permission = ACL.DEFAULT;
    let score = 0;

    for (let i = 0; i < acls.length; i++) {
      const candidate = acls[i];
      score = ACL.getMatchingScore(candidate, req);
      if (score < 0) {
        // the highest scored ACL did not match
        break;
      }
      if (!req.isWildcard()) {
        // We should stop from the first match for non-wildcard
        permission = candidate.permission;
        break;
      } else {
        if (req.exactlyMatches(candidate)) {
          permission = candidate.permission;
          break;
        }
        // For wildcard match, find the strongest permission
        const candidateOrder = AccessContext.permissionOrder[candidate.permission];
        const permissionOrder = AccessContext.permissionOrder[permission];
        if (candidateOrder > permissionOrder) {
          permission = candidate.permission;
          break;
        }
      }
    }

    if (debug.enabled) {
      debug('The following ACLs were searched: ');
      acls.forEach(function(acl) {
        acl.debug();
        debug('with score:', acl.score(req));
      });
    }
    const res = new AccessRequest({
      model: req.model,
      property: req.property,
      accessType: req.accessType,
      permission: permission || ACL.DEFAULT,
      registry: this.registry});

    // Elucidate permission status if DEFAULT
    res.settleDefaultPermission();

    return res;
  };

  /*!
   * Get the static ACLs from the model definition
   * @param {String} model The model name
   * @param {String} property The property/method/relation name
   *
   * @return {Object[]} An array of ACLs
   */
  ACL.getStaticACLs = function getStaticACLs(model, property) {
    const modelClass = this.registry.findModel(model);
    const staticACLs = [];
    if (modelClass && modelClass.settings.acls) {
      modelClass.settings.acls.forEach(function(acl) {
        let prop = acl.property;
        // We support static ACL property with array of string values.
        if (Array.isArray(prop) && prop.indexOf(property) >= 0)
          prop = property;
        if (!prop || prop === ACL.ALL || property === prop) {
          staticACLs.push(new ACL({
            model: model,
            property: prop || ACL.ALL,
            principalType: acl.principalType,
            principalId: acl.principalId, // TODO: Should it be a name?
            accessType: acl.accessType || ACL.ALL,
            permission: acl.permission,
          }));
        }
      });
    }
    const prop = modelClass && (
      // regular property
      modelClass.definition.properties[property] ||
      // relation/scope
      (modelClass._scopeMeta && modelClass._scopeMeta[property]) ||
      // static method
      modelClass[property] ||
      // prototype method
      modelClass.prototype[property]);
    if (prop && prop.acls) {
      prop.acls.forEach(function(acl) {
        staticACLs.push(new ACL({
          model: modelClass.modelName,
          property: property,
          principalType: acl.principalType,
          principalId: acl.principalId,
          accessType: acl.accessType,
          permission: acl.permission,
        }));
      });
    }
    return staticACLs;
  };

  /**
   * Check if the given principal is allowed to access the model/property
   * @param {String} principalType The principal type.
   * @param {String} principalId The principal ID.
   * @param {String} model The model name.
   * @param {String} property The property/method/relation name.
   * @param {String} accessType The access type.
   * @callback {Function} callback Callback function.
   * @param {String|Error} err The error object.
   * @param {AccessRequest} result The resolved access request.
   */
  ACL.checkPermission = function checkPermission(principalType, principalId,
    model, property, accessType,
    callback) {
    if (!callback) callback = utils.createPromiseCallback();
    if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
      principalId = principalId.toString();
    }
    property = property || ACL.ALL;
    const propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]};
    accessType = accessType || ACL.ALL;
    const accessTypeQuery = (accessType === ACL.ALL) ? undefined :
      {inq: [accessType, ACL.ALL, ACL.EXECUTE]};

    const req = new AccessRequest({model, property, accessType, registry: this.registry});

    let acls = this.getStaticACLs(model, property);

    // resolved is an instance of AccessRequest
    let resolved = this.resolvePermission(acls, req);

    if (resolved && resolved.permission === ACL.DENY) {
      debug('Permission denied by statically resolved permission');
      debug(' Resolved Permission: %j', resolved);
      process.nextTick(function() {
        callback(null, resolved);
      });
      return callback.promise;
    }

    const self = this;
    this.find({where: {principalType: principalType, principalId: principalId,
      model: model, property: propertyQuery, accessType: accessTypeQuery}},
    function(err, dynACLs) {
      if (err) {
        return callback(err);
      }
      acls = acls.concat(dynACLs);
      // resolved is an instance of AccessRequest
      resolved = self.resolvePermission(acls, req);
      return callback(null, resolved);
    });
    return callback.promise;
  };

  ACL.prototype.debug = function() {
    if (debug.enabled) {
      debug('---ACL---');
      debug('model %s', this.model);
      debug('property %s', this.property);
      debug('principalType %s', this.principalType);
      debug('principalId %s', this.principalId);
      debug('accessType %s', this.accessType);
      debug('permission %s', this.permission);
    }
  };

  // NOTE Regarding ACL.isAllowed() and ACL.prototype.isAllowed()
  // Extending existing logic, including from ACL.checkAccessForContext() method,
  // ACL instance with missing property `permission` are not promoted to
  // permission = ACL.DEFAULT config. Such ACL instances will hence always be
  // inefective

  /**
   * Test if ACL's permission is ALLOW
   * @param {String} permission The permission to test, expects one of 'ALLOW', 'DENY', 'DEFAULT'
   * @param {String} defaultPermission The default permission to apply if not providing a finite one in the permission parameter
   * @returns {Boolean} true if ACL permission is ALLOW
   */
  ACL.isAllowed = function(permission, defaultPermission) {
    if (permission === ACL.DEFAULT) {
      permission = defaultPermission || ACL.ALLOW;
    }
    return permission !== loopback.ACL.DENY;
  };

  /**
   * Test if ACL's permission is ALLOW
   * @param {String} defaultPermission The default permission to apply if missing in ACL instance
   * @returns {Boolean} true if ACL permission is ALLOW
   */
  ACL.prototype.isAllowed = function(defaultPermission) {
    return this.constructor.isAllowed(this.permission, defaultPermission);
  };

  /**
   * Check if the request has the permission to access.
   * @options {AccessContext|Object} context
   * An AccessContext instance or a plain object with the following properties.
   * @property {Object[]} principals An array of principals.
   * @property {String|Model} model The model name or model class.
   * @property {*} modelId The model instance ID.
   * @property {String} property The property/method/relation name.
   * @property {String} accessType The access type:
   * READ, REPLICATE, WRITE, or EXECUTE.
   * @callback {Function} callback Callback function
   * @param {String|Error} err The error object.
   * @param {AccessRequest} result The resolved access request.
   */
  ACL.checkAccessForContext = function(context, callback) {
    if (!callback) callback = utils.createPromiseCallback();
    const self = this;
    self.resolveRelatedModels();
    const roleModel = self.roleModel;

    if (!(context instanceof AccessContext)) {
      context.registry = this.registry;
      context = new AccessContext(context);
    }

    let authorizedRoles = {};
    const remotingContext = context.remotingContext;
    const model = context.model;
    const modelDefaultPermission = model && model.settings.defaultPermission;
    const property = context.property;
    const accessType = context.accessType;
    const modelName = context.modelName;

    const methodNames = context.methodNames;
    const propertyQuery = (property === ACL.ALL) ? undefined : {inq: methodNames.concat([ACL.ALL])};

    const accessTypeQuery = (accessType === ACL.ALL) ?
      undefined :
      (accessType === ACL.REPLICATE) ?
        {inq: [ACL.REPLICATE, ACL.WRITE, ACL.ALL]} :
        {inq: [accessType, ACL.ALL]};

    const req = new AccessRequest({
      model: modelName,
      property,
      accessType,
      permission: ACL.DEFAULT,
      methodNames,
      registry: this.registry});

    if (!context.isScopeAllowed()) {
      req.permission = ACL.DENY;
      debug('--Denied by scope config--');
      debug('Scopes allowed:', context.accessToken.scopes || ctx.DEFAULT_SCOPES);
      debug('Scope required:', context.getScopes());
      context.debug();
      callback(null, req);
      return callback.promise;
    }

    const effectiveACLs = [];
    const staticACLs = self.getStaticACLs(model.modelName, property);

    const query = {
      where: {
        model: {inq: [model.modelName, ACL.ALL]},
        property: propertyQuery,
        accessType: accessTypeQuery,
      },
    };

    this.find(query, function(err, acls) {
      if (err) return callback(err);
      const inRoleTasks = [];

      acls = acls.concat(staticACLs);

      acls.forEach(function(acl) {
        // Check exact matches
        for (let i = 0; i < context.principals.length; i++) {
          const p = context.principals[i];
          const typeMatch = p.type === acl.principalType;
          const idMatch = String(p.id) === String(acl.principalId);
          if (typeMatch && idMatch) {
            effectiveACLs.push(acl);
            return;
          }
        }

        // Check role matches
        if (acl.principalType === ACL.ROLE) {
          inRoleTasks.push(function(done) {
            roleModel.isInRole(acl.principalId, context,
              function(err, inRole) {
                if (!err && inRole) {
                  effectiveACLs.push(acl);
                  // add the role to authorizedRoles if allowed
                  if (acl.isAllowed(modelDefaultPermission))
                    authorizedRoles[acl.principalId] = true;
                }
                done(err, acl);
              });
          });
        }
      });

      async.parallel(inRoleTasks, function(err, results) {
        if (err) return callback(err, null);

        // resolved is an instance of AccessRequest
        const resolved = self.resolvePermission(effectiveACLs, req);
        debug('---Resolved---');
        resolved.debug();

        // set authorizedRoles in remotingContext options argument if
        // resolved AccessRequest permission is ALLOW, else set it to empty object
        authorizedRoles = resolved.isAllowed() ? authorizedRoles : {};
        saveAuthorizedRolesToRemotingContext(remotingContext, authorizedRoles);
        return callback(null, resolved);
      });
    });
    return callback.promise;
  };

  function saveAuthorizedRolesToRemotingContext(remotingContext, authorizedRoles) {
    const options = remotingContext && remotingContext.args && remotingContext.args.options;
    // authorizedRoles key/value map is added to the options argument only if
    // the latter exists and is an object. This means that the feature's availability
    // will depend on the app configuration
    if (options && typeof options === 'object') { // null is object too
      options.authorizedRoles = authorizedRoles;
    }
  }

  /**
   * Check if the given access token can invoke the method
   * @param {AccessToken} token The access token
   * @param {String} model The model name
   * @param {*} modelId The model id
   * @param {String} method The method name
   * @callback {Function} callback Callback function
   * @param {String|Error} err The error object
   * @param {Boolean} allowed is the request allowed
   */
  ACL.checkAccessForToken = function(token, model, modelId, method, callback) {
    assert(token, 'Access token is required');
    if (!callback) callback = utils.createPromiseCallback();
    const context = new AccessContext({
      registry: this.registry,
      accessToken: token,
      model: model,
      property: method,
      method: method,
      modelId: modelId,
    });

    this.checkAccessForContext(context, function(err, accessRequest) {
      if (err) callback(err);
      else callback(null, accessRequest.isAllowed());
    });
    return callback.promise;
  };

  ACL.resolveRelatedModels = function() {
    if (!this.roleModel) {
      const reg = this.registry;
      this.roleModel = reg.getModelByType('Role');
      this.roleMappingModel = reg.getModelByType('RoleMapping');
      this.userModel = reg.getModelByType('User');
      this.applicationModel = reg.getModelByType('Application');
    }
  };

  /**
   * Resolve a principal by type/id
   * @param {String} type Principal type - ROLE/APP/USER
   * @param {String|Number} id Principal id or name
   * @callback {Function} callback Callback function
   * @param {String|Error} err The error object
   * @param {Object} result An instance of principal (Role, Application or User)
   */
  ACL.resolvePrincipal = function(type, id, cb) {
    cb = cb || utils.createPromiseCallback();
    type = type || ACL.ROLE;
    this.resolveRelatedModels();

    switch (type) {
      case ACL.ROLE:
        this.roleModel.findOne({where: {or: [{name: id}, {id: id}]}}, cb);
        break;
      case ACL.USER:
        this.userModel.findOne(
          {where: {or: [{username: id}, {email: id}, {id: id}]}}, cb,
        );
        break;
      case ACL.APP:
        this.applicationModel.findOne(
          {where: {or: [{name: id}, {email: id}, {id: id}]}}, cb,
        );
        break;
      default:
        // try resolving a user model with a name matching the principalType
        const userModel = this.registry.findModel(type);
        if (userModel) {
          userModel.findOne(
            {where: {or: [{username: id}, {email: id}, {id: id}]}},
            cb,
          );
        } else {
          process.nextTick(function() {
            const err = new Error(g.f('Invalid principal type: %s', type));
            err.statusCode = 400;
            err.code = 'INVALID_PRINCIPAL_TYPE';
            cb(err);
          });
        }
    }
    return cb.promise;
  };

  /**
   * Check if the given principal is mapped to the role
   * @param {String} principalType Principal type
   * @param {String|*} principalId Principal id/name
   * @param {String|*} role Role id/name
   * @callback {Function} callback Callback function
   * @param {String|Error} err The error object
   * @param {Boolean} isMapped is the ACL mapped to the role
   */
  ACL.isMappedToRole = function(principalType, principalId, role, cb) {
    cb = cb || utils.createPromiseCallback();
    const self = this;
    this.resolvePrincipal(principalType, principalId,
      function(err, principal) {
        if (err) return cb(err);
        if (principal != null) {
          principalId = principal.id;
        }
        principalType = principalType || 'ROLE';
        self.resolvePrincipal('ROLE', role, function(err, role) {
          if (err || !role) return cb(err, role);
          self.roleMappingModel.findOne({
            where: {
              roleId: role.id,
              principalType: principalType,
              principalId: String(principalId),
            },
          }, function(err, result) {
            if (err) return cb(err);
            return cb(null, !!result);
          });
        });
      });
    return cb.promise;
  };
};


================================================
FILE: common/models/acl.json
================================================
{
  "name": "ACL",
  "properties": {
    "model": {
      "type": "string",
      "description": "The name of the model"
    },
    "property": {
      "type": "string",
      "description": "The name of the property, method, scope, or relation"
    },
    "accessType": "string",
    "permission": "string",
    "principalType": "string",
    "principalId": "string"
  }
}


================================================
FILE: common/models/application.js
================================================
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';
const assert = require('assert');
const utils = require('../../lib/utils');

/*!
 * Application management functions
 */

const crypto = require('crypto');

function generateKey(hmacKey, algorithm, encoding) {
  hmacKey = hmacKey || 'loopback';
  algorithm = algorithm || 'sha1';
  encoding = encoding || 'hex';
  const hmac = crypto.createHmac(algorithm, hmacKey);
  const buf = crypto.randomBytes(32);
  hmac.update(buf);
  const key = hmac.digest(encoding);
  return key;
}

/**
 * Manage client applications and organize their users.
 *
 * @property {String} id  Generated ID.
 * @property {String} name Name; required.
 * @property {String} description Text description
 * @property {String} icon String Icon image URL.
 * @property {String} owner User ID of the developer who registers the application.
 * @property {String} email E-mail address
 * @property {Boolean} emailVerified Whether the e-mail is verified.
 * @property {String} url OAuth 2.0  application URL.
 * @property {String}[] callbackUrls The OAuth 2.0 code/token callback URL.
 * @property {String} status Status of the application; Either `production`, `sandbox` (default), or `disabled`.
 * @property {Date} created Date Application object was created.  Default: current date.
 * @property {Date} modified Date Application object was modified.  Default: current date.
 *
 * @property {Object} pushSettings.apns APNS configuration, see the options
 *   below and also
 *   https://github.com/argon/node-apn/blob/master/doc/apn.markdown
 * @property {Boolean} pushSettings.apns.production Whether to use production Apple Push Notification Service (APNS) servers to send push notifications.
 * If true, uses `gateway.push.apple.com:2195` and `feedback.push.apple.com:2196`.
 * If false, uses `gateway.sandbox.push.apple.com:2195` and `feedback.sandbox.push.apple.com:2196`
 * @property {String} pushSettings.apns.certData The certificate data loaded from the cert.pem file (APNS).
 * @property {String} pushSettings.apns.keyData The key data loaded from the key.pem file (APNS).
 * @property {String} pushSettings.apns.pushOptions.gateway (APNS).
 * @property {Number} pushSettings.apns.pushOptions.port (APNS).
 * @property {String} pushSettings.apns.feedbackOptions.gateway  (APNS).
 * @property {Number} pushSettings.apns.feedbackOptions.port (APNS).
 * @property {Boolean} pushSettings.apns.feedbackOptions.batchFeedback (APNS).
 * @property {Number} pushSettings.apns.feedbackOptions.interval (APNS).
 * @property {String} pushSettings.gcm.serverApiKey: Google Cloud Messaging API key.
 *
 * @property {Boolean} authenticationEnabled
 * @property {Boolean} anonymousAllowed
 * @property {Array} authenticationSchemes List of authentication schemes
 *  (see below).
 * @property {String} authenticationSchemes.scheme Scheme name.
 *   Supported values: `local`, `facebook`, `google`,
 *   `twitter`, `linkedin`, `github`.
 * @property {Object} authenticationSchemes.credential
 *   Scheme-specific credentials.
 *
 * @class Application
 * @inherits {PersistedModel}
 */

module.exports = function(Application) {
  /*!
   * A hook to generate keys before creation
   * @param next
   */
  Application.observe('before save', function(ctx, next) {
    if (!ctx.instance) {
      // Partial update - don't generate new keys
      // NOTE(bajtos) This also means that an atomic updateOrCreate
      // will not generate keys when a new record is creatd
      return next();
    }

    const app = ctx.instance;
    app.created = app.modified = new Date();
    if (!app.id) {
      app.id = generateKey('id', 'md5');
    }
    app.clientKey = generateKey('client');
    app.javaScriptKey = generateKey('javaScript');
    app.restApiKey = generateKey('restApi');
    app.windowsKey = generateKey('windows');
    app.masterKey = generateKey('master');
    next();
  });

  /**
   * Register a new application
   * @param {String} owner Owner's user ID.
   * @param {String} name  Name of the application
   * @param {Object} options  Other options
   * @callback {Function} callback  Callback function
   * @param {Error} err
   * @promise
   */
  Application.register = function(owner, name, options, cb) {
    assert(owner, 'owner is required');
    assert(name, 'name is required');

    if (typeof options === 'function' && !cb) {
      cb = options;
      options = {};
    }
    cb = cb || utils.createPromiseCallback();

    const props = {owner: owner, name: name};
    for (const p in options) {
      if (!(p in props)) {
        props[p] = options[p];
      }
    }
    this.create(props, cb);
    return cb.promise;
  };

  /**
   * Reset keys for the application instance
   * @callback {Function} callback
   * @param {Error} err
   */
  Application.prototype.resetKeys = function(cb) {
    this.clientKey = generateKey('client');
    this.javaScriptKey = generateKey('javaScript');
    this.restApiKey = generateKey('restApi');
    this.windowsKey = generateKey('windows');
    this.masterKey = generateKey('master');
    this.modified = new Date();
    this.save(cb);
  };

  /**
   * Reset keys for a given application by the appId
   * @param {Any} appId
   * @callback {Function} callback
   * @param {Error} err
   * @promise
   */
  Application.resetKeys = function(appId, cb) {
    cb = cb || utils.createPromiseCallback();
    this.findById(appId, function(err, app) {
      if (err) {
        if (cb) cb(err, app);
        return;
      }
      app.resetKeys(cb);
    });
    return cb.promise;
  };

  /**
   * Authenticate the application id and key.
   *
   * @param {Any} appId
   * @param {String} key
   * @callback {Function} callback
   * @param {Error} err
   * @param {String} matched The matching key; one of:
   * - clientKey
   * - javaScriptKey
   * - restApiKey
   * - windowsKey
   * - masterKey
   * @promise
   */
  Application.authenticate = function(appId, key, cb) {
    cb = cb || utils.createPromiseCallback();

    this.findById(appId, function(err, app) {
      if (err || !app) {
        cb(err, null);
        return cb.promise;
      }
      let result = null;
      const keyNames = ['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey'];
      for (let i = 0; i < keyNames.length; i++) {
        if (app[keyNames[i]] === key) {
          result = {
            application: app,
            keyType: keyNames[i],
          };
          break;
        }
      }
      cb(null, result);
    });
    return cb.promise;
  };
};


================================================
FILE: common/models/application.json
================================================
{
  "name": "Application",
  "properties": {
    "id": {
      "type": "string",
      "id": true
    },
    "realm": {
      "type": "string"
    },
    "name": {
      "type": "string",
      "required": true
    },
    "description": "string",
    "icon": {
      "type": "string",
      "description": "The icon image url"
    },

    "owner": {
      "type": "string",
      "description": "The user id of the developer who registers the application"
    },
    "collaborators": {
      "type": ["string"],
      "description": "A list of users ids who have permissions to work on this app"
    },

    "email": "string",
    "emailVerified": "boolean",

    "url": {
      "type": "string",
      "description": "The application URL for OAuth 2.0"
    },
    "callbackUrls": {
      "type": ["string"],
      "description": "OAuth 2.0 code/token callback URLs"
    },
    "permissions": {
      "type": ["string"],
      "description": "A list of permissions required by the application"
    },

    "clientKey": "string",
    "javaScriptKey": "string",
    "restApiKey": "string",
    "windowsKey": "string",
    "masterKey": "string",

    "pushSettings": {
      "apns": {
        "production": {
          "type": "boolean",
          "description": [
            "Production or development mode. It denotes what default APNS",
            "servers to be used to send notifications.",
            "See API documentation for more details."
          ]
        },

        "certData": {
          "type": "string",
          "description": "The certificate data loaded from the cert.pem file"
        },
        "keyData": {
          "type": "string",
          "description": "The key data loaded from the key.pem file"
        },

        "pushOptions": {
          "type": {
            "gateway": "string",
            "port": "number"
          }
        },

        "feedbackOptions": {
          "type": {
            "gateway": "string",
            "port": "number",
            "batchFeedback": "boolean",
            "interval": "number"
          }
        }
      },

      "gcm": {
        "serverApiKey": "string"
      }
    },

    "authenticationEnabled": {
      "type": "boolean",
      "default": true
    },
    "anonymousAllowed": {
      "type": "boolean",
      "default": true
    },
    "authenticationSchemes": [
      {
        "scheme": {
          "type": "string",
          "description": "See the API docs for the list of supported values."
        },
        "credential": {
          "type": "object",
          "description": "Scheme-specific credentials"
        }
      }
    ],

    "status": {
      "type": "string",
      "default": "sandbox",
      "description": "Status of the application, production/sandbox/disabled"
    },

    "created": {
      "type": "date",
      "defaultFn": "now"
    },
    "modified": {
      "type": "date",
      "defaultFn": "now"
    }
  }
}


================================================
FILE: common/models/change.js
================================================
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

/*!
 * Module Dependencies.
 */

'use strict';
const g = require('../../lib/globalize');
const PersistedModel = require('../../lib/loopback').PersistedModel;
const loopback = require('../../lib/loopback');
const utils = require('../../lib/utils');
const crypto = require('crypto');
const CJSON = {stringify: require('canonical-json')};
const async = require('async');
const assert = require('assert');
const debug = require('debug')('loopback:change');

/**
 * Change list entry.
 *
 * @property {String} id Hash of the modelName and ID.
 * @property {String} rev The current model revision.
 * @property {String} prev The previous model revision.
 * @property {Number} checkpoint The current checkpoint at time of the change.
 * @property {String} modelName Model name.
 * @property {String} modelId Model ID.
 * @property {Object} settings Extends the `Model.settings` object.
 * @property {String} settings.hashAlgorithm Algorithm used to create cryptographic hash, used as argument
 * to [crypto.createHash](http://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm).  Default is sha1.
 * @property {Boolean} settings.ignoreErrors By default, when changes are rectified, an error will throw an exception.
 * However, if this setting is true, then errors will not throw exceptions.
 * @class Change
 * @inherits {PersistedModel}
 */

module.exports = function(Change) {
  /*!
   * Constants
   */

  Change.UPDATE = 'update';
  Change.CREATE = 'create';
  Change.DELETE = 'delete';
  Change.UNKNOWN = 'unknown';

  /*!
   * Conflict Class
   */

  Change.Conflict = Conflict;

  /*!
   * Setup the extended model.
   */

  Change.setup = function() {
    PersistedModel.setup.call(this);
    const Change = this;

    Change.getter.id = function() {
      const hasModel = this.modelName && this.modelId;
      if (!hasModel) return null;

      return Change.idForModel(this.modelName, this.modelId);
    };
  };
  Change.setup();

  /**
   * Track the recent change of the given modelIds.
   *
   * @param  {String}   modelName
   * @param  {Array}    modelIds
   * @callback {Function} callback
   * @param {Error} err
   * @param {Array} changes Changes that were tracked
   */

  Change.rectifyModelChanges = function(modelName, modelIds, callback) {
    const Change = this;
    const errors = [];

    callback = callback || utils.createPromiseCallback();

    const tasks = modelIds.map(function(id) {
      return function(cb) {
        Change.findOrCreateChange(modelName, id, function(err, change) {
          if (err) return next(err);
          change.rectify(next);
        });

        function next(err) {
          if (err) {
            err.modelName = modelName;
            err.modelId = id;
            errors.push(err);
          }
          cb();
        }
      };
    });

    async.parallel(tasks, function(err) {
      if (err) return callback(err);
      if (errors.length) {
        const desc = errors
          .map(function(e) {
            return '#' + e.modelId + ' - ' + e.toString();
          })
          .join('\n');

        const msg = g.f('Cannot rectify %s changes:\n%s', modelName, desc);
        err = new Error(msg);
        err.details = {errors: errors};
        return callback(err);
      }
      callback();
    });
    return callback.promise;
  };

  /**
   * Get an identifier for a given model.
   *
   * @param  {String} modelName
   * @param  {String} modelId
   * @return {String}
   */

  Change.idForModel = function(modelName, modelId) {
    return this.hash([modelName, modelId].join('-'));
  };

  /**
   * Find or create a change for the given model.
   *
   * @param  {String}   modelName
   * @param  {String}   modelId
   * @callback  {Function} callback
   * @param {Error} err
   * @param {Change} change
   * @end
   */

  Change.findOrCreateChange = function(modelName, modelId, callback) {
    assert(this.registry.findModel(modelName), modelName + ' does not exist');
    callback = callback || utils.createPromiseCallback();
    const id = this.idForModel(modelName, modelId);
    const Change = this;

    this.findById(id, function(err, change) {
      if (err) return callback(err);
      if (change) {
        callback(null, change);
      } else {
        const ch = new Change({
          id: id,
          modelName: modelName,
          modelId: modelId,
        });
        ch.debug('creating change');
        Change.updateOrCreate(ch, callback);
      }
    });
    return callback.promise;
  };

  /**
   * Update (or create) the change with the current revision.
   *
   * @callback {Function} callback
   * @param {Error} err
   * @param {Change} change
   */

  Change.prototype.rectify = function(cb) {
    const change = this;
    const currentRev = this.rev;

    change.debug('rectify change');

    cb = cb || utils.createPromiseCallback();

    const model = this.getModelCtor();
    const id = this.getModelId();

    model.findById(id, function(err, inst) {
      if (err) return cb(err);

      if (inst) {
        inst.fillCustomChangeProperties(change, function() {
          const rev = Change.revisionForInst(inst);
          prepareAndDoRectify(rev);
        });
      } else {
        prepareAndDoRectify(null);
      }
    });

    return cb.promise;

    function prepareAndDoRectify(rev) {
      // avoid setting rev and prev to the same value
      if (currentRev === rev) {
        change.debug('rev and prev are equal (not updating anything)');
        return cb(null, change);
      }

      // FIXME(@bajtos) Allow callers to pass in the checkpoint value
      // (or even better - a memoized async function to get the cp value)
      // That will enable `rectifyAll` to cache the checkpoint value
      change.constructor.getCheckpointModel().current(
        function(err, checkpoint) {
          if (err) return cb(err);
          doRectify(checkpoint, rev);
        },
      );
    }

    function doRectify(checkpoint, rev) {
      if (rev) {
        if (currentRev === rev) {
          change.debug('ASSERTION FAILED: Change currentRev==rev ' +
            'should have been already handled');
          return cb(null, change);
        } else {
          change.rev = rev;
          change.debug('updated revision (was ' + currentRev + ')');
          if (change.checkpoint !== checkpoint) {
            // previous revision is updated only across checkpoints
            change.prev = currentRev;
            change.debug('updated prev');
          }
        }
      } else {
        change.rev = null;
        change.debug('updated revision (was ' + currentRev + ')');
        if (change.checkpoint !== checkpoint) {
          // previous revision is updated only across checkpoints
          if (currentRev) {
            change.prev = currentRev;
          } else if (!change.prev) {
            change.debug('ERROR - could not determine prev');
            change.prev = Change.UNKNOWN;
          }
          change.debug('updated prev');
        }
      }

      if (change.checkpoint != checkpoint) {
        debug('update checkpoint to', checkpoint);
        change.checkpoint = checkpoint;
      }

      if (change.prev === Change.UNKNOWN) {
        // this occurs when a record of a change doesn't exist
        // and its current revision is null (not found)
        change.remove(cb);
      } else {
        change.save(cb);
      }
    }
  };

  /**
   * Get a change's current revision based on current data.
   * @callback  {Function} callback
   * @param {Error} err
   * @param {String} rev The current revision
   */

  Change.prototype.currentRevision = function(cb) {
    cb = cb || utils.createPromiseCallback();
    const model = this.getModelCtor();
    const id = this.getModelId();
    model.findById(id, function(err, inst) {
      if (err) return cb(err);
      if (inst) {
        cb(null, Change.revisionForInst(inst));
      } else {
        cb(null, null);
      }
    });
    return cb.promise;
  };

  /**
   * Create a hash of the given `string` with the `options.hashAlgorithm`.
   * **Default: `sha1`**
   *
   * @param  {String} str The string to be hashed
   * @return {String}     The hashed string
   */

  Change.hash = function(str) {
    return crypto
      .createHash(Change.settings.hashAlgorithm || 'sha1')
      .update(str)
      .digest('hex');
  };

  /**
   * Get the revision string for the given object
   * @param  {Object} inst The data to get the revision string for
   * @return {String}      The revision string
   */

  Change.revisionForInst = function(inst) {
    assert(inst, 'Change.revisionForInst() requires an instance object.');
    return this.hash(CJSON.stringify(inst));
  };

  /**
   * Get a change's type. Returns one of:
   *
   * - `Change.UPDATE`
   * - `Change.CREATE`
   * - `Change.DELETE`
   * - `Change.UNKNOWN`
   *
   * @return {String} the type of change
   */

  Change.prototype.type = function() {
    if (this.rev && this.prev) {
      return Change.UPDATE;
    }
    if (this.rev && !this.prev) {
      return Change.CREATE;
    }
    if (!this.rev && this.prev) {
      return Change.DELETE;
    }
    return Change.UNKNOWN;
  };

  /**
   * Compare two changes.
   * @param  {Change} change
   * @return {Boolean}
   */

  Change.prototype.equals = function(change) {
    if (!change) return false;
    const thisRev = this.rev || null;
    const thatRev = change.rev || null;
    return thisRev === thatRev;
  };

  /**
   * Does this change conflict with the given change.
   * @param  {Change} change
   * @return {Boolean}
   */

  Change.prototype.conflictsWith = function(change) {
    if (!change) return false;
    if (this.equals(change)) return false;
    if (Change.bothDeleted(this, change)) return false;
    if (this.isBasedOn(change)) return false;
    return true;
  };

  /**
   * Are both changes deletes?
   * @param  {Change} a
   * @param  {Change} b
   * @return {Boolean}
   */

  Change.bothDeleted = function(a, b) {
    return a.type() === Change.DELETE &&
      b.type() === Change.DELETE;
  };

  /**
   * Determine if the change is based on the given change.
   * @param  {Change} change
   * @return {Boolean}
   */

  Change.prototype.isBasedOn = function(change) {
    return this.prev === change.rev;
  };

  /**
   * Determine the differences for a given model since a given checkpoint.
   *
   * The callback will contain an error or `result`.
   *
   * **result**
   *
   * ```js
   * {
 *   deltas: Array,
 *   conflicts: Array
 * }
   * ```
   *
   * **deltas**
   *
   * An array of changes that differ from `remoteChanges`.
   *
   * **conflicts**
   *
   * An array of changes that conflict with `remoteChanges`.
   *
   * @param  {String}   modelName
   * @param  {Number}   since         Compare changes after this checkpoint
   * @param  {Change[]} remoteChanges A set of changes to compare
   * @callback  {Function} callback
   * @param {Error} err
   * @param {Object} result See above.
   */

  Change.diff = function(modelName, since, remoteChanges, callback) {
    callback = callback || utils.createPromiseCallback();

    if (!Array.isArray(remoteChanges) || remoteChanges.length === 0) {
      callback(null, {deltas: [], conflicts: []});
      return callback.promise;
    }
    const remoteChangeIndex = {};
    const modelIds = [];
    remoteChanges.forEach(function(ch) {
      modelIds.push(ch.modelId);
      remoteChangeIndex[ch.modelId] = new Change(ch);
    });

    // normalize `since`
    since = Number(since) || 0;
    this.find({
      where: {
        modelName: modelName,
        modelId: {inq: modelIds},
      },
    }, function(err, allLocalChanges) {
      if (err) return callback(err);
      const deltas = [];
      const conflicts = [];
      const localModelIds = [];

      const localChanges = allLocalChanges.filter(function(c) {
        return c.checkpoint >= since;
      });

      localChanges.forEach(function(localChange) {
        localChange = new Change(localChange);
        localModelIds.push(localChange.modelId);
        const remoteChange = remoteChangeIndex[localChange.modelId];
        if (remoteChange && !localChange.equals(remoteChange)) {
          if (remoteChange.conflictsWith(localChange)) {
            remoteChange.debug('remote conflict');
            localChange.debug('local conflict');
            conflicts.push(localChange);
          } else {
            remoteChange.debug('remote delta');
            deltas.push(remoteChange);
          }
        }
      });

      modelIds.forEach(function(id) {
        if (localModelIds.indexOf(id) !== -1) return;

        const d = remoteChangeIndex[id];
        const oldChange = allLocalChanges.filter(function(c) {
          return c.modelId === id;
        })[0];

        if (oldChange) {
          d.prev = oldChange.rev;
        } else {
          d.prev = null;
        }

        deltas.push(d);
      });

      callback(null, {
        deltas: deltas,
        conflicts: conflicts,
      });
    });
    return callback.promise;
  };

  /**
   * Correct all change list entries.
   * @param {Function} cb
   */

  Change.rectifyAll = function(cb) {
    debug('rectify all');
    const Change = this;
    // this should be optimized
    this.find(function(err, changes) {
      if (err) return cb(err);
      async.each(
        changes,
        function(c, next) { c.rectify(next); },
        cb,
      );
    });
  };

  /**
   * Get the checkpoint model.
   * @return {Checkpoint}
   */

  Change.getCheckpointModel = function() {
    let checkpointModel = this.Checkpoint;
    if (checkpointModel) return checkpointModel;
    // FIXME(bajtos) This code creates multiple different models with the same
    // model name, which is not a valid supported usage of juggler's API.
    this.Checkpoint = checkpointModel = loopback.Checkpoint.extend('checkpoint');
    assert(this.dataSource, 'Cannot getCheckpointModel(): ' + this.modelName +
      ' is not attached to a dataSource');
    checkpointModel.attachTo(this.dataSource);
    return checkpointModel;
  };

  Change.prototype.debug = function() {
    if (debug.enabled) {
      const args = Array.prototype.slice.call(arguments);
      args[0] = args[0] + ' %s';
      args.push(this.modelName);
      debug.apply(this, args);
      debug('\tid', this.id);
      debug('\trev', this.rev);
      debug('\tprev', this.prev);
      debug('\tcheckpoint', this.checkpoint);
      debug('\tmodelName', this.modelName);
      debug('\tmodelId', this.modelId);
      debug('\ttype', this.type());
    }
  };

  /**
   * Get the `Model` class for `change.modelName`.
   * @return {Model}
   */

  Change.prototype.getModelCtor = function() {
    return this.constructor.settings.trackModel;
  };

  Change.prototype.getModelId = function() {
    // TODO(ritch) get rid of the need to create an instance
    const Model = this.getModelCtor();
    const id = this.modelId;
    const m = new Model();
    m.setId(id);
    return m.getId();
  };

  Change.prototype.getModel = function(callback) {
    const Model = this.constructor.settings.trackModel;
    const id = this.getModelId();
    Model.findById(id, callback);
  };

  /**
   * When two changes conflict a conflict is created.
   *
   * **Note**: call `conflict.fetch()` to get the `target` and `source` models.
   *
   * @param {*} modelId
   * @param {PersistedModel} SourceModel
   * @param {PersistedModel} TargetModel
   * @property {ModelClass} source The source model instance
   * @property {ModelClass} target The target model instance
   * @class Change.Conflict
   */

  function Conflict(modelId, SourceModel, TargetModel) {
    this.SourceModel = SourceModel;
    this.TargetModel = TargetModel;
    this.SourceChange = SourceModel.getChangeModel();
    this.TargetChange = TargetModel.getChangeModel();
    this.modelId = modelId;
  }

  /**
   * Fetch the conflicting models.
   *
   * @callback {Function} callback
   * @param {Error} err
   * @param {PersistedModel} source
   * @param {PersistedModel} target
   */

  Conflict.prototype.models = function(cb) {
    const conflict = this;
    const SourceModel = this.SourceModel;
    const TargetModel = this.TargetModel;
    let source, target;

    async.parallel([
      getSourceModel,
      getTargetModel,
    ], done);

    function getSourceModel(cb) {
      SourceModel.findById(conflict.modelId, function(err, model) {
        if (err) return cb(err);
        source = model;
        cb();
      });
    }

    function getTargetModel(cb) {
      TargetModel.findById(conflict.modelId, function(err, model) {
        if (err) return cb(err);
        target = model;
        cb();
      });
    }

    function done(err) {
      if (err) return cb(err);
      cb(null, source, target);
    }
  };

  /**
   * Get the conflicting changes.
   *
   * @callback {Function} callback
   * @param {Error} err
   * @param {Change} sourceChange
   * @param {Change} targetChange
   */

  Conflict.prototype.changes = function(cb) {
    const conflict = this;
    let sourceChange, targetChange;

    async.parallel([
      getSourceChange,
      getTargetChange,
    ], done);

    function getSourceChange(cb) {
      const SourceModel = conflict.SourceModel;
      SourceModel.findLastChange(conflict.modelId, function(err, change) {
        if (err) return cb(err);
        sourceChange = change;
        cb();
      });
    }

    function getTargetChange(cb) {
      const TargetModel = conflict.TargetModel;
      TargetModel.findLastChange(conflict.modelId, function(err, change) {
        if (err) return cb(err);
        targetChange = change;
        cb();
      });
    }

    function done(err) {
      if (err) return cb(err);
      cb(null, sourceChange, targetChange);
    }
  };

  /**
   * Resolve the conflict.
   *
   * Set the source change's previous revision to the current revision of the
   * (conflicting) target change. Since the changes are no longer conflicting
   * and appear as if the source change was based on the target, they will be
   * replicated normally as part of the next replicate() call.
   *
   * This is effectively resolving the conflict using the source version.
   *
   * @callback {Function} callback
   * @param {Error} err
   */

  Conflict.prototype.resolve = function(cb) {
    const conflict = this;
    conflict.TargetModel.findLastChange(
      this.modelId,
      function(err, targetChange) {
        if (err) return cb(err);
        conflict.SourceModel.updateLastChange(
          conflict.modelId,
          {prev: targetChange.rev},
          cb,
        );
      },
    );
  };

  /**
   * Resolve the conflict using the instance data in the source model.
   *
   * @callback {Function} callback
   * @param {Error} err
   */
  Conflict.prototype.resolveUsingSource = function(cb) {
    this.resolve(function(err) {
      // don't forward any cb arguments from resolve()
      cb(err);
    });
  };

  /**
   * Resolve the conflict using the instance data in the target model.
   *
   * @callback {Function} callback
   * @param {Error} err
   */
  Conflict.prototype.resolveUsingTarget = function(cb) {
    const conflict = this;

    conflict.models(function(err, source, target) {
      if (err) return done(err);
      if (target === null) {
        return conflict.SourceModel.deleteById(conflict.modelId, done);
      }
      const inst = new conflict.SourceModel(
        target.toObject(),
        {persisted: true},
      );
      inst.save(done);
    });

    function done(err) {
      // don't forward any cb arguments from internal calls
      cb(err);
    }
  };

  /**
   * Return a new Conflict instance with swapped Source and Target models.
   *
   * This is useful when resolving a conflict in one-way
   * replication, where the source data must not be changed:
   *
   * ```js
   * conflict.swapParties().resolveUsingTarget(cb);
   * ```
   *
   * @returns {Conflict} A new Conflict instance.
   */
  Conflict.prototype.swapParties = function() {
    const Ctor = this.constructor;
    return new Ctor(this.modelId, this.TargetModel, this.SourceModel);
  };

  /**
   * Resolve the conflict using the supplied instance data.
   *
   * @param {Object} data The set of changes to apply on the model
   * instance. Use `null` value to delete the source instance instead.
   * @callback {Function} callback
   * @param {Error} err
   */

  Conflict.prototype.resolveManually = function(data, cb) {
    const conflict = this;
    if (!data) {
      return conflict.SourceModel.deleteById(conflict.modelId, done);
    }

    conflict.models(function(err, source, target) {
      if (err) return done(err);
      const inst = source || new conflict.SourceModel(target);
      inst.setAttributes(data);
      inst.save(function(err) {
        if (err) return done(err);
        conflict.resolve(done);
      });
    });

    function done(err) {
      // don't forward any cb arguments from internal calls
      cb(err);
    }
  };

  /**
   * Determine the conflict type.
   *
   * Possible results are
   *
   *  - `Change.UPDATE`: Source and target models were updated.
   *  - `Change.DELETE`: Source and or target model was deleted.
   *  - `Change.UNKNOWN`: the conflict type is uknown or due to an error.
   *
   * @callback {Function} callback
   * @param {Error} err
   * @param {String} type The conflict type.
   */

  Conflict.prototype.type = function(cb) {
    const conflict = this;
    this.changes(function(err, sourceChange, targetChange) {
      if (err) return cb(err);
      const sourceChangeType = sourceChange.type();
      const targetChangeType = targetChange.type();
      if (sourceChangeType === Change.UPDATE && targetChangeType === Change.UPDATE) {
        return cb(null, Change.UPDATE);
      }
      if (sourceChangeType === Change.DELETE || targetChangeType === Change.DELETE) {
        return cb(null, Change.DELETE);
      }
      return cb(null, Change.UNKNOWN);
    });
  };
};


================================================
FILE: common/models/change.json
================================================
{
  "name": "Change",
  "trackChanges": false,
  "properties": {
    "id": {
      "type": "string",
      "id": true
    },
    "rev": {
      "type": "string"
    },
    "prev": {
      "type": "string"
    },
    "checkpoint": {
      "type": "number"
    },
    "modelName": {
      "type": "string"
    },
    "modelId": {
      "type": "string"
    }
  }
}


================================================
FILE: common/models/checkpoint.js
================================================
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

/**
 * Module Dependencies.
 */

'use strict';
const assert = require('assert');

/**
 * Checkpoint list entry.
 *
 * @property id {Number} the sequencial identifier of a checkpoint
 * @property time {Number} the time when the checkpoint was created
 * @property sourceId {String}  the source identifier
 *
 * @class Checkpoint
 * @inherits {PersistedModel}
 */

module.exports = function(Checkpoint) {
  // Workaround for https://github.com/strongloop/loopback/issues/292
  Checkpoint.definition.rawProperties.time.default =
    Checkpoint.definition.properties.time.default = function() {
      return new Date();
    };

  /**
   * Get the current checkpoint id
   * @callback {Function} callback
   * @param {Error} err
   * @param {Number} checkpoint The current checkpoint seq
   */
  Checkpoint.current = function(cb) {
    const Checkpoint = this;
    Checkpoint._getSingleton(function(err, cp) {
      cb(err, cp.seq);
    });
  };

  Checkpoint._getSingleton = function(cb) {
    const query = {limit: 1}; // match all instances, return only one
    const initialData = {seq: 1};
    this.findOrCreate(query, initialData, cb);
  };

  /**
   * Increase the current checkpoint if it already exists otherwise initialize it
   * @callback {Function} callback
   * @param {Error} err
   * @param {Object} checkpoint The current checkpoint
   */
  Checkpoint.bumpLastSeq = function(cb) {
    const Checkpoint = this;
    Checkpoint._getSingleton(function(err, cp) {
      if (err) return cb(err);
      const originalSeq = cp.seq;
      cp.seq++;
      // Update the checkpoint but only if it was not changed under our hands
      Checkpoint.updateAll({id: cp.id, seq: originalSeq}, {seq: cp.seq}, function(err, info) {
        if (err) return cb(err);
        // possible outcomes
        // 1) seq was updated to seq+1 - exactly what we wanted!
        // 2) somebody else already updated seq to seq+1 and our call was a no-op.
        //   That should be ok, checkpoints are time based, so we reuse the one created just now
        //  3) seq was bumped more than once, so we will be using a value that is behind the latest seq.
        //    @bajtos is not entirely sure if this is ok, but since it wasn't handled by the current implementation either,
        //    he thinks we can keep it this way.
        cb(null, cp);
      });
    });
  };
};


================================================
FILE: common/models/checkpoint.json
================================================
{
  "name": "Checkpoint",
  "properties": {
    "seq": {
      "type": "number"
    },
    "time": {
      "type": "date"
    },
    "sourceId": {
      "type": "string"
    }
  }
}


================================================
FILE: common/models/email.js
================================================
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';
const g = require('../../lib/globalize');

/**
* Email model.  Extends LoopBack base [Model](#model-new-model).
* @property {String} to Email addressee.  Required.
* @property {String} from Email sender address.  Required.
* @property {String} subject Email subject string.  Required.
* @property {String} text Text body of email.
* @property {String} html HTML body of email.
*
* @class Email
* @inherits {Model}
*/

module.exports = function(Email) {
  /**
   * Send an email with the given `options`.
   *
   * Example Options:
   *
   * ```js
   * {
   *   from: "Fred Foo <foo@blurdybloop.com>", // sender address
   *   to: "bar@blurdybloop.com, baz@blurdybloop.com", // list of receivers
   *   subject: "Hello", // Subject line
   *   text: "Hello world", // plaintext body
   *   html: "<b>Hello world</b>" // html body
   * }
   * ```
   *
   * See https://github.com/andris9/Nodemailer for other supported options.
   *
   * @options {Object} options See below
   * @prop {String} from Senders's email address
   * @prop {String} to List of one or more recipient email addresses (comma-delimited)
   * @prop {String} subject Subject line
   * @prop {String} text Body text
   * @prop {String} html Body HTML (optional)
   * @param {Function} callback Called after the e-mail is sent or the sending failed
   */

  Email.send = function() {
    throw new Error(g.f('You must connect the {{Email}} Model to a {{Mail}} connector'));
  };

  /**
   * A shortcut for Email.send(this).
   */
  Email.prototype.send = function() {
    throw new Error(g.f('You must connect the {{Email}} Model to a {{Mail}} connector'));
  };
};


================================================
FILE: common/models/email.json
================================================
{
  "name": "Email",
  "base": "Model",
  "properties": {
    "to": {"type": "String", "required": true},
    "from": {"type": "String", "required": true},
    "subject": {"type": "String", "required": true},
    "text": {"type": "String"},
    "html": {"type": "String"}
  }
}


================================================
FILE: common/models/key-value-model.js
================================================
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';
const g = require('../../lib/globalize');

/**
 * Data model for key-value databases.
 *
 * @class KeyValueModel
 * @inherits {Model}
 */

module.exports = function(KeyValueModel) {
  /**
   * Return the value associated with a given key.
   *
   * @param {String} key Key to use when searching the database.
   * @options {Object} options
   * @callback {Function} callback
   * @param {Error} err Error object.
   * @param {Any} result Value associated with the given key.
   * @promise
   *
   * @header KeyValueModel.get(key, cb)
   */
  KeyValueModel.get = function(key, options, callback) {
    throwNotAttached(this.modelName, 'get');
  };

  /**
   * Persist a value and associate it with the given key.
   *
   * @param {String} key Key to associate with the given value.
   * @param {Any} value Value to persist.
   * @options {Number|Object} options Optional settings for the key-value
   *   pair. If a Number is provided, it is set as the TTL (time to live) in ms
   *   (milliseconds) for the key-value pair.
   * @property {Number} ttl TTL for the key-value pair in ms.
   * @callback {Function} callback
   * @param {Error} err Error object.
   * @promise
   *
   * @header KeyValueModel.set(key, value, cb)
   */
  KeyValueModel.set = function(key, value, options, callback) {
    throwNotAttached(this.modelName, 'set');
  };

  /**
   * Set the TTL (time to live) in ms (milliseconds) for a given key. TTL is the
   * remaining time before a key-value pair is discarded from the database.
   *
   * @param {String} key Key to use when searching the database.
   * @param {Number} ttl TTL in ms to set for the key.
   * @options {Object} options
   * @callback {Function} callback
   * @param {Error} err Error object.
   * @promise
   *
   * @header KeyValueModel.expire(key, ttl, cb)
   */
  KeyValueModel.expire = function(key, ttl, options, callback) {
    throwNotAttached(this.modelName, 'expire');
  };

  /**
   * Return the TTL (time to live) for a given key. TTL is the remaining time
   * before a key-value pair is discarded from the database.
   *
   * @param {String} key Key to use when searching the database.
   * @options {Object} options
   * @callback {Function} callback
   * @param {Error} error
   * @param {Number} ttl Expiration time for the key-value pair. `undefined` if
   *   TTL was not initially set.
   * @promise
   *
   * @header KeyValueModel.ttl(key, cb)
   */
  KeyValueModel.ttl = function(key, options, callback) {
    throwNotAttached(this.modelName, 'ttl');
  };

  /**
   * Return all keys in the database.
   *
   * **WARNING**: This method is not suitable for large data sets as all
   * key-values pairs are loaded into memory at once. For large data sets,
   * use `iterateKeys()` instead.
   *
   * @param {Object} filter An optional filter object with the following
   * @param {String} filter.match Glob string used to filter returned
   *   keys (i.e. `userid.*`). All connectors are required to support `*` and
   *   `?`, but may also support additional special characters specific to the
   *   database.
   * @param {Object} options
   * @callback {Function} callback
   * @promise
   *
   * @header KeyValueModel.keys(filter, cb)
   */
  KeyValueModel.keys = function(filter, options, callback) {
    throwNotAttached(this.modelName, 'keys');
  };

  /**
   * Asynchronously iterate all keys in the database. Similar to `.keys()` but
   * instead allows for iteration over large data sets without having to load
   * everything into memory at once.
   *
   * Callback example:
   * ```js
   * // Given a model named `Color` with two keys `red` and `blue`
   * var iterator = Color.iterateKeys();
   * it.next(function(err, key) {
   *   // key contains `red`
   *   it.next(function(err, key) {
   *     // key contains `blue`
   *   });
   * });
   * ```
   *
   * Promise example:
   * ```js
   * // Given a model named `Color` with two keys `red` and `blue`
   * var iterator = Color.iterateKeys();
   * Promise.resolve().then(function() {
   *   return it.next();
   * })
   * .then(function(key) {
   *   // key contains `red`
   *   return it.next();
   * });
   * .then(function(key) {
   *   // key contains `blue`
   * });
   * ```
   *
   * @param {Object} filter An optional filter object with the following
   * @param {String} filter.match Glob string to use to filter returned
   *   keys (i.e. `userid.*`). All connectors are required to support `*` and
   *   `?`. They may also support additional special characters that are
   *   specific to the backing database.
   * @param {Object} options
   * @returns {AsyncIterator} An Object implementing `next(cb) -> Promise`
   *   function that can be used to iterate all keys.
   *
   * @header KeyValueModel.iterateKeys(filter)
   */
  KeyValueModel.iterateKeys = function(filter, options) {
    throwNotAttached(this.modelName, 'iterateKeys');
  };

  /*!
   * Set up remoting metadata for this model.
   *
   * **Notes**:
   * - The method is called automatically by `Model.extend` and/or
   *   `app.registry.createModel`
   * - In general, base models use call this to ensure remote methods are
   *   inherited correctly, see bug at
   *   https://github.com/strongloop/loopback/issues/2350
   */
  KeyValueModel.setup = function() {
    KeyValueModel.base.setup.apply(this, arguments);

    this.remoteMethod('get', {
      accepts: {
        arg: 'key', type: 'string', required: true,
        http: {source: 'path'},
      },
      returns: {arg: 'value', type: 'any', root: true},
      http: {path: '/:key', verb: 'get'},
      rest: {after: convertNullToNotFoundError},
    });

    this.remoteMethod('set', {
      accepts: [
        {arg: 'key', type: 'string', required: true,
          http: {source: 'path'}},
        {arg: 'value', type: 'any', required: true,
          http: {source: 'body'}},
        {arg: 'ttl', type: 'number',
          http: {source: 'query'},
          description: 'time to live in milliseconds'},
      ],
      http: {path: '/:key', verb: 'put'},
    });

    this.remoteMethod('expire', {
      accepts: [
        {arg: 'key', type: 'string', required: true,
          http: {source: 'path'}},
        {arg: 'ttl', type: 'number', required: true,
          http: {source: 'form'}},
      ],
      http: {path: '/:key/expire', verb: 'put'},
    });

    this.remoteMethod('ttl', {
      accepts: {
        arg: 'key', type: 'string', required: true,
        http: {source: 'path'},
      },
      returns: {arg: 'value', type: 'any', root: true},
      http: {path: '/:key/ttl', verb: 'get'},
    });

    this.remoteMethod('keys', {
      accepts: {
        arg: 'filter', type: 'object', required: false,
        http: {source: 'query'},
      },
      returns: {arg: 'keys', type: ['string'], root: true},
      http: {path: '/keys', verb: 'get'},
    });
  };
};

function throwNotAttached(modelName, methodName) {
  throw new Error(g.f(
    'Cannot call %s.%s(). ' +
      'The %s method has not been setup. ' +
      'The {{KeyValueModel}} has not been correctly attached ' +
      'to a {{DataSource}}!',
    modelName, methodName, methodName,
  ));
}

function convertNullToNotFoundError(ctx, cb) {
  if (ctx.result !== null) return cb();

  const modelName = ctx.method.sharedClass.name;
  const id = ctx.getArgByName('id');
  const msg = g.f('Unknown "%s" {{key}} "%s".', modelName, id);
  const error = new Error(msg);
  error.statusCode = error.status = 404;
  error.code = 'KEY_NOT_FOUND';
  cb(error);
}


================================================
FI
Download .txt
gitextract_fjzzp53t/

├── .eslintignore
├── .eslintrc
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── Bug_report.md
│   │   ├── Feature_request.md
│   │   ├── Question.md
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── stale.yml
├── .gitignore
├── .npmrc
├── .nycrc
├── .travis.yml
├── CHANGES.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── common/
│   └── models/
│       ├── README.md
│       ├── access-token.js
│       ├── access-token.json
│       ├── acl.js
│       ├── acl.json
│       ├── application.js
│       ├── application.json
│       ├── change.js
│       ├── change.json
│       ├── checkpoint.js
│       ├── checkpoint.json
│       ├── email.js
│       ├── email.json
│       ├── key-value-model.js
│       ├── key-value-model.json
│       ├── role-mapping.js
│       ├── role-mapping.json
│       ├── role.js
│       ├── role.json
│       ├── scope.js
│       ├── scope.json
│       ├── user.js
│       └── user.json
├── docs/
│   └── api-explorer-details.md
├── docs.json
├── example/
│   ├── client-server/
│   │   ├── client.js
│   │   ├── models.js
│   │   └── server.js
│   ├── colors/
│   │   └── app.js
│   ├── mobile-models/
│   │   └── app.js
│   ├── replication/
│   │   └── app.js
│   └── simple-data-source/
│       └── app.js
├── index.js
├── intl/
│   ├── cs/
│   │   └── messages.json
│   ├── de/
│   │   └── messages.json
│   ├── en/
│   │   └── messages.json
│   ├── es/
│   │   └── messages.json
│   ├── fr/
│   │   └── messages.json
│   ├── it/
│   │   └── messages.json
│   ├── ja/
│   │   └── messages.json
│   ├── ko/
│   │   └── messages.json
│   ├── nl/
│   │   └── messages.json
│   ├── pl/
│   │   └── messages.json
│   ├── pt/
│   │   └── messages.json
│   ├── ru/
│   │   └── messages.json
│   ├── tr/
│   │   └── messages.json
│   ├── zh-Hans/
│   │   └── messages.json
│   └── zh-Hant/
│       └── messages.json
├── lib/
│   ├── access-context.js
│   ├── application.js
│   ├── browser-express.js
│   ├── builtin-models.js
│   ├── configure-shared-methods.js
│   ├── connectors/
│   │   ├── base-connector.js
│   │   ├── mail.js
│   │   └── memory.js
│   ├── current-context.js
│   ├── globalize.js
│   ├── loopback.js
│   ├── model.js
│   ├── persisted-model.js
│   ├── registry.js
│   ├── runtime.js
│   ├── server-app.js
│   └── utils.js
├── package.json
├── server/
│   └── middleware/
│       ├── context.js
│       ├── error-handler.js
│       ├── favicon.js
│       ├── rest.js
│       ├── static.js
│       ├── status.js
│       ├── token.js
│       └── url-not-found.js
├── templates/
│   ├── reset-form.ejs
│   └── verify.ejs
└── test/
    ├── access-control.integration.js
    ├── access-token.test.js
    ├── acl.test.js
    ├── app.test.js
    ├── authorization-scopes.test.js
    ├── change-stream.test.js
    ├── change.test.js
    ├── checkpoint.test.js
    ├── context-options.test.js
    ├── data-source.test.js
    ├── e2e/
    │   ├── remote-connector.e2e.js
    │   └── replication.e2e.js
    ├── email.test.js
    ├── error-handler.test.js
    ├── fixtures/
    │   ├── access-control/
    │   │   ├── common/
    │   │   │   └── models/
    │   │   │       ├── access-token.json
    │   │   │       ├── account.json
    │   │   │       ├── accountWithReplaceOnPUTfalse.json
    │   │   │       ├── alert.json
    │   │   │       ├── bank.json
    │   │   │       ├── email.json
    │   │   │       ├── transaction.json
    │   │   │       └── user.json
    │   │   └── server/
    │   │       ├── config.json
    │   │       ├── datasources.json
    │   │       ├── model-config.json
    │   │       └── server.js
    │   ├── e2e/
    │   │   └── server/
    │   │       ├── models.js
    │   │       └── server.js
    │   ├── shared-methods/
    │   │   ├── both-configs-set/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-default-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-default-true/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-defined-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── config-defined-true/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── model-config-default-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── model-config-default-true/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   ├── model-config-defined-false/
    │   │   │   ├── common/
    │   │   │   │   └── models/
    │   │   │   │       ├── todo.js
    │   │   │   │       └── todo.json
    │   │   │   └── server/
    │   │   │       ├── config.json
    │   │   │       ├── datasources.json
    │   │   │       ├── model-config.json
    │   │   │       └── server.js
    │   │   └── model-config-defined-true/
    │   │       ├── common/
    │   │       │   └── models/
    │   │       │       ├── todo.js
    │   │       │       └── todo.json
    │   │       └── server/
    │   │           ├── config.json
    │   │           ├── datasources.json
    │   │           ├── model-config.json
    │   │           └── server.js
    │   ├── simple-app/
    │   │   ├── boot/
    │   │   │   ├── bad.txt
    │   │   │   └── foo.js
    │   │   ├── common/
    │   │   │   └── models/
    │   │   │       ├── bar.js
    │   │   │       ├── bar.json
    │   │   │       └── foo.json
    │   │   └── server/
    │   │       ├── config.json
    │   │       ├── datasources.json
    │   │       └── model-config.json
    │   ├── simple-integration-app/
    │   │   ├── common/
    │   │   │   └── models/
    │   │   │       ├── access-token.json
    │   │   │       ├── appointment.json
    │   │   │       ├── customer-forceid.json
    │   │   │       ├── customer.json
    │   │   │       ├── email.json
    │   │   │       ├── patient.json
    │   │   │       ├── physician.json
    │   │   │       ├── profile.json
    │   │   │       ├── store-replacing.json
    │   │   │       ├── store-updating.json
    │   │   │       ├── store.json
    │   │   │       ├── user.json
    │   │   │       └── widget.json
    │   │   └── server/
    │   │       ├── config.json
    │   │       ├── datasources.json
    │   │       ├── model-config.json
    │   │       └── server.js
    │   └── user-integration-app/
    │       ├── common/
    │       │   └── models/
    │       │       ├── blog.json
    │       │       ├── my-user.json
    │       │       └── post.json
    │       └── server/
    │           ├── config.json
    │           ├── datasources.json
    │           ├── model-config.json
    │           └── server.js
    ├── geo-point.test.js
    ├── helpers/
    │   ├── error-loggers.js
    │   ├── expect.js
    │   ├── loopback-testing-helper.js
    │   ├── use-english.js
    │   └── wait-for-event.js
    ├── hidden-properties.test.js
    ├── integration.test.js
    ├── karma.conf.js
    ├── key-value-model.test.js
    ├── loopback.test.js
    ├── memory.test.js
    ├── mocha.opts
    ├── model.application.test.js
    ├── model.test.js
    ├── multiple-user-principal-accessing-another-user-model.js
    ├── multiple-user-principal-types.test.js
    ├── registries.test.js
    ├── relations.integration.js
    ├── remote-connector.test.js
    ├── remoting-coercion.test.js
    ├── remoting.integration.js
    ├── replication.rest.test.js
    ├── replication.test.js
    ├── rest.middleware.test.js
    ├── role-mapping.test.js
    ├── role.test.js
    ├── user-password.test.js
    ├── user.integration.js
    ├── user.test.js
    ├── util/
    │   ├── describe.js
    │   ├── it.js
    │   └── model-tests.js
    └── utils.test.js
Download .txt
SYMBOL INDEX (299 symbols across 55 files)

FILE: common/models/access-token.js
  constant DEFAULT_TOKEN_LEN (line 15) | const DEFAULT_TOKEN_LEN = 64;

FILE: common/models/acl.js
  function saveAuthorizedRolesToRemotingContext (line 542) | function saveAuthorizedRolesToRemotingContext(remotingContext, authorize...

FILE: common/models/application.js
  function generateKey (line 16) | function generateKey(hmacKey, algorithm, encoding) {

FILE: common/models/change.js
  constant CJSON (line 16) | const CJSON = {stringify: require('canonical-json')};
  function next (line 95) | function next(err) {
  function prepareAndDoRectify (line 205) | function prepareAndDoRectify(rev) {
  function doRectify (line 223) | function doRectify(checkpoint, rev) {
  function Conflict (line 580) | function Conflict(modelId, SourceModel, TargetModel) {
  function getSourceModel (line 608) | function getSourceModel(cb) {
  function getTargetModel (line 616) | function getTargetModel(cb) {
  function done (line 624) | function done(err) {
  function getSourceChange (line 648) | function getSourceChange(cb) {
  function getTargetChange (line 657) | function getTargetChange(cb) {
  function done (line 666) | function done(err) {
  function done (line 735) | function done(err) {
  function done (line 783) | function done(err) {

FILE: common/models/key-value-model.js
  function throwNotAttached (line 223) | function throwNotAttached(modelName, methodName) {
  function convertNullToNotFoundError (line 233) | function convertNullToNotFoundError(ctx, cb) {

FILE: common/models/role.js
  function listByPrincipalType (line 126) | function listByPrincipalType(context, model, principalType, query, callb...
  function isUserClass (line 188) | function isUserClass(modelClass) {
  function matches (line 201) | function matches(id1, id2) {
  function legacyOwnershipCheck (line 309) | function legacyOwnershipCheck(inst) {
  function checkOwnership (line 350) | function checkOwnership(inst) {
  function _isMultipleUsers (line 404) | function _isMultipleUsers(userModel) {

FILE: common/models/user.js
  constant SALT_WORK_FACTOR (line 17) | const SALT_WORK_FACTOR = 10;
  constant MAX_PASSWORD_LENGTH (line 21) | const MAX_PASSWORD_LENGTH = 72;
  constant DEFAULT_TTL (line 35) | const DEFAULT_TTL = 1209600;
  constant DEFAULT_RESET_PW_TTL (line 36) | const DEFAULT_RESET_PW_TTL = 15 * 60;
  constant DEFAULT_MAX_TTL (line 37) | const DEFAULT_MAX_TTL = 31556926;
  function splitPrincipal (line 132) | function splitPrincipal(name, realmDelimiter) {
  function tokenHandler (line 276) | function tokenHandler(err, token) {
  function addTokenToUserAndSave (line 806) | function addTokenToUserAndSave(err, token) {
  function sendEmail (line 816) | function sendEmail(user) {
  function assertVerifyOptions (line 861) | function assertVerifyOptions(verifyOptions) {
  function createVerificationEmailBody (line 875) | function createVerificationEmailBody(verifyOptions, options, cb) {
  function onTokenCreated (line 1012) | function onTokenCreated(err, accessToken) {
  function getUserIdFromRequestContext (line 1240) | function getUserIdFromRequestContext(ctx) {
  function emailValidator (line 1428) | function emailValidator(err, done) {
  function joinUrlPath (line 1439) | function joinUrlPath(args) {

FILE: example/replication/app.js
  constant SPEED (line 16) | const SPEED = process.env.SPEED || 100;
  function createSomeInitialSourceData (line 72) | function createSomeInitialSourceData() {
  function replicateSourceToTarget (line 80) | function replicateSourceToTarget() {
  function resolveAllConflicts (line 86) | function resolveAllConflicts() {
  function updateSomeTargetData (line 94) | function updateSomeTargetData() {
  function createMoreSourceData (line 101) | function createMoreSourceData() {
  function createEvenMoreSourceData (line 105) | function createEvenMoreSourceData() {
  function updateSomeSourceDataCausingAConflict (line 109) | function updateSomeSourceDataCausingAConflict() {
  function deleteAllSourceData (line 116) | function deleteAllSourceData() {
  function createSomeNewSourceData (line 120) | function createSomeNewSourceData() {
  function list (line 128) | function list(model, msg) {
  function run (line 138) | function run(steps) {

FILE: lib/access-context.js
  constant DEFAULT_SCOPES (line 11) | const DEFAULT_SCOPES = ['DEFAULT'];
  function AccessContext (line 43) | function AccessContext(context) {
  function Principal (line 279) | function Principal(type, id, name) {
  function AccessRequest (line 322) | function AccessRequest(model, property, accessType, permission, methodNa...

FILE: lib/application.js
  function App (line 44) | function App() {
  function scheduleVerification (line 466) | function scheduleVerification(Model, verifyFn) {
  function verifyAccessTokenRelations (line 474) | function verifyAccessTokenRelations(Model) {
  function verifyUserRelations (line 502) | function verifyUserRelations(Model) {
  function dataSourcesFromConfig (line 549) | function dataSourcesFromConfig(name, config, connectorRegistry, registry) {
  function configureModel (line 573) | function configureModel(ModelCtor, config, app) {
  function clearHandlerCache (line 597) | function clearHandlerCache(app) {

FILE: lib/browser-express.js
  function browserExpress (line 12) | function browserExpress() {
  function BrowserExpress (line 18) | function BrowserExpress() {

FILE: lib/builtin-models.js
  function createModel (line 68) | function createModel(definitionJson, customizeFn) {
  function cloneDeepJson (line 86) | function cloneDeepJson(obj) {

FILE: lib/configure-shared-methods.js
  function escapeRegExp (line 78) | function escapeRegExp(str) {

FILE: lib/connectors/base-connector.js
  function Connector (line 30) | function Connector(options) {

FILE: lib/connectors/mail.js
  function MailConnector (line 27) | function MailConnector(settings) {
  function Mailer (line 95) | function Mailer() {

FILE: lib/connectors/memory.js
  function Memory (line 31) | function Memory() {

FILE: lib/loopback.js
  function createApplication (line 75) | function createApplication(options) {
  function mixin (line 121) | function mixin(source) {

FILE: lib/model.js
  function setupOptionsArgs (line 480) | function setupOptionsArgs(accepts, modelClass) {
  function createOptionsViaModelMethod (line 494) | function createOptionsViaModelMethod(ctx) {
  function convertNullToNotFoundError (line 564) | function convertNullToNotFoundError(toModelName, ctx, cb) {

FILE: lib/persisted-model.js
  constant REPLICATION_CHUNK_SIZE (line 20) | const REPLICATION_CHUNK_SIZE = -1;
  function throwNotAttached (line 70) | function throwNotAttached(modelName, methodName) {
  function convertNullToNotFoundError (line 85) | function convertNullToNotFoundError(ctx, cb) {
  function save (line 451) | function save() {
  function setRemoting (line 642) | function setRemoting(scope, name, options) {
  function run (line 1189) | function run(attempt, since) {
  function tryReplicate (line 1205) | function tryReplicate(sourceModel, targetModel, since, options, callback) {
  function buildLookupOfAffectedModelData (line 1482) | function buildLookupOfAffectedModelData(Model, updates, callback) {
  function applyUpdate (line 1497) | function applyUpdate(Model, id, current, data, change, conflicts, option...
  function applyCreate (line 1552) | function applyCreate(Model, id, current, data, change, conflicts, option...
  function applyDelete (line 1581) | function applyDelete(Model, id, current, change, conflicts, options, cb) {
  function cleanup (line 1707) | function cleanup() {
  function rectifyOnSave (line 1716) | function rectifyOnSave(ctx, next) {
  function rectifyOnDelete (line 1742) | function rectifyOnDelete(ctx, next) {
  function getIdFromWhereByModelId (line 1766) | function getIdFromWhereByModelId(Model, where) {
  function attachRelatedModels (line 1804) | function attachRelatedModels(self) {
  function changeHandler (line 1902) | function changeHandler(ctx, next) {
  function deleteHandler (line 1911) | function deleteHandler(ctx, next) {
  function createChangeObject (line 1920) | function createChangeObject(ctx, type) {
  function removeHandlers (line 1969) | function removeHandlers() {

FILE: lib/registry.js
  function Registry (line 24) | function Registry() {
  function buildModelOptionsFromConfig (line 130) | function buildModelOptionsFromConfig(config) {
  function addACL (line 154) | function addACL(acls, acl) {

FILE: lib/server-app.js
  constant BUILTIN_MIDDLEWARE (line 15) | const BUILTIN_MIDDLEWARE = {builtin: true};
  function compareLayers (line 295) | function compareLayers(left, right) {

FILE: lib/utils.js
  function createPromiseCallback (line 16) | function createPromiseCallback() {
  function throwPromiseNotDefined (line 28) | function throwPromiseNotDefined() {
  function uploadInChunks (line 42) | function uploadInChunks(largeArray, chunkSize, processFunction, cb) {
  function downloadInChunks (line 94) | function downloadInChunks(filter, chunkSize, processFunction, cb) {
  function concatResults (line 129) | function concatResults(previousResults, currentResults) {

FILE: server/middleware/rest.js
  function rest (line 32) | function rest() {

FILE: server/middleware/status.js
  function status (line 26) | function status() {

FILE: server/middleware/token.js
  function rewriteUserLiteral (line 25) | function rewriteUserLiteral(req, currentUserLiteral, next) {
  function escapeRegExp (line 55) | function escapeRegExp(str) {
  function token (line 98) | function token(options) {

FILE: server/middleware/url-not-found.js
  function urlNotFound (line 19) | function urlNotFound() {

FILE: test/access-control.integration.js
  constant ACCESS_CONTROL_APP (line 10) | const ACCESS_CONTROL_APP = path.join(__dirname, 'fixtures', 'access-cont...
  constant USER (line 13) | const USER = {email: 'test@test.test', password: 'test'};
  constant CURRENT_USER (line 14) | const CURRENT_USER = {email: 'current@test.test', password: 'test'};
  function urlForUser (line 136) | function urlForUser() {
  function newUserData (line 141) | function newUserData() {
  function urlForBank (line 207) | function urlForBank() {
  function urlForAccount (line 294) | function urlForAccount() {
  function urlForReplaceAccountPOST (line 297) | function urlForReplaceAccountPOST() {
  function urlForAccount (line 360) | function urlForAccount() {
  function urlForReplaceAccountPOST (line 363) | function urlForReplaceAccountPOST() {

FILE: test/access-token.test.js
  constant ACL (line 17) | let Token, ACL, User, TestModel;
  function mockRequest (line 606) | function mockRequest(opts) {
  function createTestingToken (line 774) | function createTestingToken(done) {
  function createTestAppAndRequest (line 785) | function createTestAppAndRequest(testToken, settings, done) {
  function createTestApp (line 790) | function createTestApp(testToken, settings, done) {
  function givenLocalTokenModel (line 864) | function givenLocalTokenModel() {

FILE: test/acl.test.js
  constant ACL (line 11) | const ACL = loopback.ACL;
  function assertPermission (line 134) | function assertPermission(expectedPermission, msg) {
  function setupAppAndRequest (line 619) | function setupAppAndRequest() {
  function createACLs (line 658) | function createACLs(model, acls) {
  function makeAuthorizedHttpRequestOnMyTestModel (line 672) | function makeAuthorizedHttpRequestOnMyTestModel() {
  function setupTestModels (line 679) | function setupTestModels() {

FILE: test/app.test.js
  function handlerThatAddsHandler (line 86) | function handlerThatAddsHandler(name) {
  function namedHandler (line 455) | function namedHandler(name) {
  function pathSavingHandler (line 462) | function pathSavingHandler() {
  function getObjectAndPrototypeKeys (line 470) | function getObjectAndPrototypeKeys(obj) {
  function verifyMiddlewarePhases (line 628) | function verifyMiddlewarePhases(names, done) {
  function setupTestModel (line 834) | function setupTestModel() {
  function setupUserModels (line 1087) | function setupUserModels(app, options, done) {
  function executeMiddlewareHandlers (line 1205) | function executeMiddlewareHandlers(app, urlPath, callback) {

FILE: test/authorization-scopes.test.js
  function givenAppAndRequest (line 81) | function givenAppAndRequest() {
  function givenRemoteMethodWithCustomScope (line 93) | function givenRemoteMethodWithCustomScope() {
  function givenUser (line 113) | function givenUser() {
  function givenDefaultToken (line 118) | function givenDefaultToken() {
  function givenScopedToken (line 123) | function givenScopedToken() {

FILE: test/change-stream.test.js
  function verifyObserversRemoval (line 125) | function verifyObserversRemoval() {

FILE: test/change.test.js
  function rectify (line 262) | function rectify(next) {
  function checkpoint (line 266) | function checkpoint(next) {
  function update (line 276) | function update(next) {
  function givenChangeInstance (line 661) | function givenChangeInstance() {

FILE: test/context-options.test.js
  function givenCategoryHasManyProductsThroughAnotherModel (line 214) | function givenCategoryHasManyProductsThroughAnotherModel() {
  function givenCategoryAndProduct (line 232) | function givenCategoryAndProduct() {
  function expectOptionsInjectedFromCategory (line 239) | function expectOptionsInjectedFromCategory() {
  function givenCategoryHasOneProduct (line 287) | function givenCategoryHasOneProduct() {
  function givenCategoryId1 (line 302) | function givenCategoryId1() {
  function givenProductInCategory1 (line 306) | function givenProductInCategory1() {
  function expectOptionsInjectedFromCategory (line 310) | function expectOptionsInjectedFromCategory() {
  function givenCategoryBelongsToProduct (line 332) | function givenCategoryBelongsToProduct() {
  function givenCategoryId1 (line 347) | function givenCategoryId1() {
  function givenProductInCategory1 (line 351) | function givenProductInCategory1() {
  function expectOptionsInjectedFromCategory (line 355) | function expectOptionsInjectedFromCategory() {
  function setupAppAndRequest (line 360) | function setupAppAndRequest() {
  function resetActualOptions (line 380) | function resetActualOptions() {
  function observeOptionsBeforeSave (line 384) | function observeOptionsBeforeSave() {
  function observeOptionsBeforeDelete (line 392) | function observeOptionsBeforeDelete() {
  function observeOptionsOnAccess (line 400) | function observeOptionsOnAccess() {
  function givenProductId1 (line 408) | function givenProductId1() {
  function expectInjectedOptions (line 412) | function expectInjectedOptions(name) {

FILE: test/data-source.test.js
  function existsAndShared (line 111) | function existsAndShared(Model, name, isRemoteEnabled, isProto) {
  function assertValidDataSource (line 124) | function assertValidDataSource(dataSource) {

FILE: test/helpers/wait-for-event.js
  function waitForEvent (line 10) | function waitForEvent(emitter, event) {

FILE: test/integration.test.js
  function setupAppWithStreamingMethod (line 36) | function setupAppWithStreamingMethod() {
  function sendHttpRequestInOnePacket (line 77) | function sendHttpRequestInOnePacket(port, reqString, cb) {

FILE: test/key-value-model.test.js
  constant AN_OBJECT_VALUE (line 12) | const AN_OBJECT_VALUE = {name: 'an-object'};
  function setupAppAndCacheItem (line 146) | function setupAppAndCacheItem() {
  function setupSharedHttpServer (line 160) | function setupSharedHttpServer(done) {

FILE: test/loopback.test.js
  function getAllMethodNamesWithoutClassName (line 526) | function getAllMethodNamesWithoutClassName(TestModel) {
  function setupLoopback (line 735) | function setupLoopback() {
  function getAllMethodNamesWithoutClassName (line 739) | function getAllMethodNamesWithoutClassName(Model) {
  function setupLoopback (line 896) | function setupLoopback() {
  function getSharedMethods (line 900) | function getSharedMethods(Model) {

FILE: test/memory.test.js
  function count (line 37) | function count() {

FILE: test/model.test.js
  constant ACL (line 11) | const ACL = loopback.ACL;
  function shouldReturn (line 703) | function shouldReturn(methodName, expectedAccessType) {
  function getCurrentCheckpoint (line 751) | function getCurrentCheckpoint(cb) {
  function checkpoint (line 758) | function checkpoint(cb) {
  function setupAppAndRequest (line 1033) | function setupAppAndRequest() {
  function createUserAndAccessToken (line 1057) | function createUserAndAccessToken() {

FILE: test/multiple-user-principal-accessing-another-user-model.js
  function setupAppAndModels (line 152) | function setupAppAndModels() {
  function setupModelInstances (line 260) | function setupModelInstances() {

FILE: test/multiple-user-principal-types.test.js
  function assertGoodToken (line 109) | function assertGoodToken(accessToken, user) {
  function waitForResetRequestAndVerify (line 160) | function waitForResetRequestAndVerify() {
  function addToAccessContext (line 264) | function addToAccessContext(list) {
  function isOwnerForMessage (line 554) | function isOwnerForMessage(user, msg) {
  function createModelWithOptions (line 571) | function createModelWithOptions(name, options) {
  function givenResetPasswordTokenForOneUser (line 666) | function givenResetPasswordTokenForOneUser() {
  function givenTokenForOneUser (line 714) | function givenTokenForOneUser() {
  function givenProductModelAllowingOnlyUserRoleAccess (line 744) | function givenProductModelAllowingOnlyUserRoleAccess() {
  function createUserModel (line 770) | function createUserModel(app, name, options) {
  function waitForEvent (line 777) | function waitForEvent(emitter, name) {
  function getIds (line 783) | function getIds(array) {

FILE: test/relations.integration.js
  constant SIMPLE_APP (line 10) | const SIMPLE_APP = path.join(__dirname, 'fixtures', 'simple-integration-...
  function setup (line 350) | function setup(connecting, cb) {

FILE: test/remoting.integration.js
  constant SIMPLE_APP (line 10) | const SIMPLE_APP = path.join(__dirname, 'fixtures', 'simple-integration-...
  function formatReturns (line 285) | function formatReturns(m) {
  function formatMethod (line 300) | function formatMethod(m) {
  function findClass (line 323) | function findClass(name) {
  function getFormattedMethodsExcludingRelations (line 331) | function getFormattedMethodsExcludingRelations(methods) {
  function getCreateMethod (line 343) | function getCreateMethod(methods) {
  function getFormattedScopeMethods (line 349) | function getFormattedScopeMethods(methods) {
  function getFormattedPrototypeMethods (line 361) | function getFormattedPrototypeMethods(methods) {

FILE: test/replication.rest.test.js
  function listCars (line 79) | function listCars() {
  function createCar (line 83) | function createCar() {
  function setupModifiedLocalCopyOfAlice (line 402) | function setupModifiedLocalCopyOfAlice(done) {
  function setupServer (line 473) | function setupServer(done) {
  function setupClient (line 522) | function setupClient() {
  function createRemoteModelOpts (line 557) | function createRemoteModelOpts(modelOpts) {
  function seedServerData (line 568) | function seedServerData(done) {
  function seedClientData (line 620) | function seedClientData(done) {
  function seedConflict (line 640) | function seedConflict(done) {
  function setAccessToken (line 665) | function setAccessToken(token) {
  function expectHttpError (line 672) | function expectHttpError(code, done) {
  function replicateServerToLocal (line 682) | function replicateServerToLocal(next) {
  function conflictError (line 692) | function conflictError(conflicts) {
  function carToString (line 698) | function carToString(c) {

FILE: test/replication.test.js
  function mockRectifyAllChanges (line 127) | function mockRectifyAllChanges(Model) {
  function mockSourceModelRectify (line 238) | function mockSourceModelRectify() {
  function mockTargetModelRectify (line 254) | function mockTargetModelRectify() {
  function bumpSourceCheckpoint (line 550) | function bumpSourceCheckpoint(cb) {
  function bumpTargetCheckpoint (line 560) | function bumpTargetCheckpoint(cb) {
  function createConflict (line 781) | function createConflict(err, conflicts) {
  function createConflict (line 859) | function createConflict() {
  function createConflict (line 936) | function createConflict() {
  function createConflict (line 1012) | function createConflict() {
  function givenReplicatedInstance (line 1196) | function givenReplicatedInstance(cb) {
  function assertChangeRecordedForId (line 1208) | function assertChangeRecordedForId(id, cb) {
  function testUpdateConflictIsResolved (line 1477) | function testUpdateConflictIsResolved(resolver, cb) {
  function testDeleteConflictIsResolved (line 1512) | function testDeleteConflictIsResolved(resolver, cb) {
  function updateClientB (line 1550) | function updateClientB(name) {
  function sync (line 1561) | function sync(client, server) {
  function updateSourceInstanceNameTo (line 1573) | function updateSourceInstanceNameTo(value) {
  function deleteSourceInstance (line 1581) | function deleteSourceInstance(value) {
  function verifySourceWasReplicated (line 1592) | function verifySourceWasReplicated(target) {
  function mockBulkUpdate (line 1791) | function mockBulkUpdate(modelToMock) {
  function replicate (line 1805) | function replicate(source, target, since, next) {
  function createModel (line 1832) | function createModel(Model, data) {
  function replicateExpectingSuccess (line 1838) | function replicateExpectingSuccess(source, target, since) {
  function setupRaceConditionInReplication (line 1856) | function setupRaceConditionInReplication(fn) {
  function verifyInstanceWasReplicated (line 1873) | function verifyInstanceWasReplicated(source, target, id) {
  function spyAndStoreSinceArg (line 1891) | function spyAndStoreSinceArg(Model, methodName, store) {
  function getPropValue (line 1899) | function getPropValue(obj, name) {
  function getIds (line 1905) | function getIds(list) {
  function assertTargetModelEqualsSourceModel (line 1909) | function assertTargetModelEqualsSourceModel(conflicts, sourceModel,
  function givenSomeSourceModelInstances (line 2050) | function givenSomeSourceModelInstances(done) {
  function mockChangeFind (line 2059) | function mockChangeFind(Model) {

FILE: test/rest.middleware.test.js
  function givenUserModelWithAuth (line 263) | function givenUserModelWithAuth() {
  function givenLoggedInUser (line 283) | function givenLoggedInUser(cb, done) {
  function getFixturePath (line 295) | function getFixturePath(dirName) {

FILE: test/role-mapping.test.js
  function setupModel (line 41) | function setupModel(modelName) {

FILE: test/role.test.js
  function checkResult (line 15) | function checkResult(err, result) {
  function givenUsers (line 763) | function givenUsers() {
  function isOwnerForMessage (line 773) | function isOwnerForMessage(user, msg) {
  function givenModelWithSenderReceiverRelations (line 790) | function givenModelWithSenderReceiverRelations(name, options) {

FILE: test/user-password.test.js
  function givenAppWithRestrictionEnabled (line 63) | function givenAppWithRestrictionEnabled() {
  function givenAppWithRejectionEnabled (line 95) | function givenAppWithRejectionEnabled() {
  function givenAppWithNoRestrictions (line 149) | function givenAppWithNoRestrictions() {
  function givenAppWithUser (line 157) | function givenAppWithUser(userSettings) {
  function givenRegularAccessToken (line 202) | function givenRegularAccessToken() {
  function givenResetPasswordToken (line 206) | function givenResetPasswordToken() {
  function changeName (line 214) | function changeName(token) {
  function patchPassword (line 220) | function patchPassword(token) {
  function changePassword (line 226) | function changePassword(token) {
  function resetPassword (line 232) | function resetPassword(token) {
  function patchNameAndPasswordDirectly (line 238) | function patchNameAndPasswordDirectly() {

FILE: test/user.integration.js
  constant SIMPLE_APP (line 10) | const SIMPLE_APP = path.join(__dirname, 'fixtures', 'user-integration-ap...
  function triggerPasswordReset (line 343) | function triggerPasswordReset(email) {

FILE: test/user.test.js
  function assertGoodToken (line 952) | function assertGoodToken(accessToken, user) {
  function login (line 1292) | function login(fn) {
  function logout (line 1296) | function logout(err, accessToken) {
  function login (line 1305) | function login(fn) {
  function logout (line 1309) | function logout(err, accessToken) {
  function login (line 1320) | function login(fn) {
  function logout (line 1336) | function logout(err, token) {
  function verify (line 1361) | function verify(token, done) {
  function saveObservedOptionsForHook (line 1543) | function saveObservedOptionsForHook(name) {
  function givenUserIdAndPassword (line 1551) | function givenUserIdAndPassword() {
  function saveObservedOptionsForHook (line 1631) | function saveObservedOptionsForHook(name) {
  function givenUserId (line 1639) | function givenUserId() {
  function givenUser (line 2234) | function givenUser() {
  function testConfirm (line 2263) | function testConfirm(testFunc, done) {
  function saveObservedOptionsForHook (line 2945) | function saveObservedOptionsForHook(name, model) {
  function assertPreservedTokens (line 3024) | function assertPreservedTokens(done) {
  function assertNoAccessTokens (line 3036) | function assertNoAccessTokens(done) {
  function createOriginalUser (line 3117) | function createOriginalUser(done) {
  function triggerPasswordReset (line 3190) | function triggerPasswordReset(email) {

FILE: test/util/model-tests.js
  constant ACL (line 11) | const ACL = loopback.ACL;

FILE: test/utils.test.js
  function processFunction (line 64) | function processFunction(filter, cb) {
Condensed preview — 242 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,359K chars).
[
  {
    "path": ".eslintignore",
    "chars": 14,
    "preview": "dist\ncoverage\n"
  },
  {
    "path": ".eslintrc",
    "chars": 208,
    "preview": "{\n  \"extends\": \"loopback\",\n  \"rules\": {\n    \"max-len\": [\"error\", 100, 4, {\n      \"ignoreComments\": true,\n      \"ignoreUr"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_report.md",
    "chars": 1391,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\nlabels: bug\n\n---\n\n<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨\n\nAre you us"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature_request.md",
    "chars": 646,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\nlabels: feature\n\n---\n\n<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨\n\nLoo"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Question.md",
    "chars": 590,
    "preview": "---\nname: Question\nabout: The issue tracker is not for questions. Please use Stack Overflow or other resources for help."
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 717,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Report a security vulnerability\n    url: https://loopback.io/doc/en"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 678,
    "preview": "<!--\nPlease provide a high-level description of the changes made by your pull request.\n\nInclude references to all relate"
  },
  {
    "path": ".github/stale.yml",
    "chars": 997,
    "preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a "
  },
  {
    "path": ".gitignore",
    "chars": 136,
    "preview": ".idea\n.project\n.DS_Store\n.vscode/\n*.sublime*\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.swp\n*.swo\nnode_modules\ndist\n*xunit.x"
  },
  {
    "path": ".npmrc",
    "chars": 19,
    "preview": "package-lock=false\n"
  },
  {
    "path": ".nycrc",
    "chars": 80,
    "preview": "{\n  \"exclude\":  [\n    \"Gruntfile.js\",\n    \"test/**/*.js\"\n  ],\n  \"cache\": true\n}\n"
  },
  {
    "path": ".travis.yml",
    "chars": 209,
    "preview": "sudo: false\nlanguage: node_js\nnode_js:\n  - \"8\"\n  - \"10\"\n  - \"12\"\n  - \"14\"\n\naddons:\n  chrome: stable\n\nafter_success: npm "
  },
  {
    "path": "CHANGES.md",
    "chars": 88042,
    "preview": "2020-11-25, Version 3.28.0\n==========================\n\n * upgrade nodemailer to greater than 6.4.16 (jannyHou)\n\n * chore"
  },
  {
    "path": "CODEOWNERS",
    "chars": 258,
    "preview": "# Lines starting with '#' are comments.\n# Each line is a file pattern followed by one or more owners,\n# the last matchin"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 7067,
    "preview": "### Contributing ###\n\nThank you for your interest in `loopback`, an open source project\nadministered by StrongLoop.\n\nCon"
  },
  {
    "path": "Gruntfile.js",
    "chars": 6770,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "LICENSE",
    "chars": 1190,
    "preview": "Copyright (c) IBM Corp. 2013,2018. All Rights Reserved.\nNode module: loopback\nThis project is licensed under the MIT Lic"
  },
  {
    "path": "README.md",
    "chars": 6358,
    "preview": "# LoopBack\n\n[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/strongloop/loopback?utm_source=badge"
  },
  {
    "path": "common/models/README.md",
    "chars": 3187,
    "preview": "# Application\n\nApplication model represents the metadata for a client application that has its\nown identity and associat"
  },
  {
    "path": "common/models/access-token.js",
    "chars": 8493,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/access-token.json",
    "chars": 698,
    "preview": "{\n  \"name\": \"AccessToken\",\n  \"properties\": {\n    \"id\": {\n      \"type\": \"string\",\n      \"id\": true\n    },\n    \"ttl\": {\n  "
  },
  {
    "path": "common/models/acl.js",
    "chars": 23045,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/acl.json",
    "chars": 374,
    "preview": "{\n  \"name\": \"ACL\",\n  \"properties\": {\n    \"model\": {\n      \"type\": \"string\",\n      \"description\": \"The name of the model\""
  },
  {
    "path": "common/models/application.js",
    "chars": 6672,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/application.json",
    "chars": 2930,
    "preview": "{\n  \"name\": \"Application\",\n  \"properties\": {\n    \"id\": {\n      \"type\": \"string\",\n      \"id\": true\n    },\n    \"realm\": {\n"
  },
  {
    "path": "common/models/change.js",
    "chars": 22155,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/change.json",
    "chars": 363,
    "preview": "{\n  \"name\": \"Change\",\n  \"trackChanges\": false,\n  \"properties\": {\n    \"id\": {\n      \"type\": \"string\",\n      \"id\": true\n  "
  },
  {
    "path": "common/models/checkpoint.js",
    "chars": 2552,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/checkpoint.json",
    "chars": 182,
    "preview": "{\n  \"name\": \"Checkpoint\",\n  \"properties\": {\n    \"seq\": {\n      \"type\": \"number\"\n    },\n    \"time\": {\n      \"type\": \"date"
  },
  {
    "path": "common/models/email.js",
    "chars": 1841,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/email.json",
    "chars": 278,
    "preview": "{\n  \"name\": \"Email\",\n  \"base\": \"Model\",\n  \"properties\": {\n    \"to\": {\"type\": \"String\", \"required\": true},\n    \"from\": {\""
  },
  {
    "path": "common/models/key-value-model.js",
    "chars": 7709,
    "preview": "// Copyright IBM Corp. 2016,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/key-value-model.json",
    "chars": 49,
    "preview": "{\n  \"name\": \"KeyValueModel\",\n  \"base\": \"Model\"\n}\n"
  },
  {
    "path": "common/models/role-mapping.js",
    "chars": 3073,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/role-mapping.json",
    "chars": 553,
    "preview": "{\n  \"name\": \"RoleMapping\",\n  \"description\": \"Map principals to roles\",\n  \"properties\": {\n    \"id\": {\n      \"type\": \"stri"
  },
  {
    "path": "common/models/role.js",
    "chars": 23194,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/role.json",
    "chars": 499,
    "preview": "{\n  \"name\": \"Role\",\n  \"properties\": {\n\n    \"id\": {\n      \"type\": \"string\",\n      \"id\": true,\n      \"generated\": true\n   "
  },
  {
    "path": "common/models/scope.js",
    "chars": 1712,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/scope.json",
    "chars": 290,
    "preview": "{\n  \"name\": \"Scope\",\n  \"description\": [\n    \"Schema for Scope which represents the permissions that are granted\",\n    \"t"
  },
  {
    "path": "common/models/user.js",
    "chars": 49721,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "common/models/user.json",
    "chars": 2526,
    "preview": "{\n  \"name\": \"User\",\n  \"properties\": {\n    \"realm\": {\n      \"type\": \"string\"\n    },\n    \"username\": {\n      \"type\": \"stri"
  },
  {
    "path": "docs/api-explorer-details.md",
    "chars": 3040,
    "preview": "<!-- NOTE: This file is not currently included into the docs.  Need to (a) decide if this info is important and if so (b"
  },
  {
    "path": "docs.json",
    "chars": 928,
    "preview": "{\n  \"title\": \"LoopBack Documentation\",\n  \"content\": [\n    \"lib/application.js\",\n    \"lib/server-app.js\",\n    \"lib/loopba"
  },
  {
    "path": "example/client-server/client.js",
    "chars": 724,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "example/client-server/models.js",
    "chars": 1022,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "example/client-server/server.js",
    "chars": 789,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "example/colors/app.js",
    "chars": 702,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "example/mobile-models/app.js",
    "chars": 1399,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "example/replication/app.js",
    "chars": 3467,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "example/simple-data-source/app.js",
    "chars": 707,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "index.js",
    "chars": 851,
    "preview": "// Copyright IBM Corp. 2013,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "intl/cs/messages.json",
    "chars": 8039,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Nebyl nalezen žádný záznam změny pro {0} s ID {1}\",\r\n  \"04bd8af876f001ceaf443a"
  },
  {
    "path": "intl/de/messages.json",
    "chars": 8886,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Kein Änderungssatz gefunden für {0} mit ID {1}\",\r\n  \"04bd8af876f001ceaf443aad6"
  },
  {
    "path": "intl/en/messages.json",
    "chars": 7889,
    "preview": "{\n  \"03f79fa268fe199de2ce4345515431c1\": \"No change record found for {0} with id {1}\",\n  \"04bd8af876f001ceaf443aad6a9002f"
  },
  {
    "path": "intl/es/messages.json",
    "chars": 8890,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"No se ha encontrado ningún registro de cambio para {0} con el id {1}\",\r\n  \"04b"
  },
  {
    "path": "intl/fr/messages.json",
    "chars": 8896,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Aucun enregistrement de changement trouvé pour {0} avec l'id {1}\",\r\n  \"04bd8af"
  },
  {
    "path": "intl/it/messages.json",
    "chars": 8760,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Nessun record di modifica trovato per {0} con id {1}\",\r\n  \"04bd8af876f001ceaf4"
  },
  {
    "path": "intl/ja/messages.json",
    "chars": 6714,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"ID {1} の {0} の変更レコードが見つかりませんでした\",\r\n  \"04bd8af876f001ceaf443aad6a9002f9\": \"認証では"
  },
  {
    "path": "intl/ko/messages.json",
    "chars": 6691,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"ID가 {1}인 {0}에 대한 변경 레코드를 찾을 수 없음\",\r\n  \"04bd8af876f001ceaf443aad6a9002f9\": \"인증을"
  },
  {
    "path": "intl/nl/messages.json",
    "chars": 8689,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Geen wijzigingsrecord gevonden voor {0} met ID {1}\",\r\n  \"04bd8af876f001ceaf443"
  },
  {
    "path": "intl/pl/messages.json",
    "chars": 8840,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Nie znaleziono rekordu zmiany dla elementu {0} o identyfikatorze {1}\",\r\n  \"04b"
  },
  {
    "path": "intl/pt/messages.json",
    "chars": 8520,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Nenhum registro de mudança localizado para {0} com o ID {1}\",\r\n  \"04bd8af876f0"
  },
  {
    "path": "intl/ru/messages.json",
    "chars": 8626,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"Не удалось найти записи изменений для {0} с ИД {1}\",\r\n  \"04bd8af876f001ceaf443"
  },
  {
    "path": "intl/tr/messages.json",
    "chars": 8538,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"{0} için {1} tanıtıcılı bir değişiklik kaydı bulunamadı\",\r\n  \"04bd8af876f001ce"
  },
  {
    "path": "intl/zh-Hans/messages.json",
    "chars": 5800,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"对于标识为 {1} 的 {0},找不到任何更改记录\",\r\n  \"04bd8af876f001ceaf443aad6a9002f9\": \"认证需要定义模型 {"
  },
  {
    "path": "intl/zh-Hant/messages.json",
    "chars": 5902,
    "preview": "{\r\n  \"03f79fa268fe199de2ce4345515431c1\": \"對於 id 為 {1} 的 {0},找不到變更記錄\",\r\n  \"04bd8af876f001ceaf443aad6a9002f9\": \"需要定義模型 {0}"
  },
  {
    "path": "lib/access-context.js",
    "chars": 13550,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/application.js",
    "chars": 19414,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/browser-express.js",
    "chars": 781,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/builtin-models.js",
    "chars": 2854,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/configure-shared-methods.js",
    "chars": 2862,
    "preview": "// Copyright IBM Corp. 2017,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/connectors/base-connector.js",
    "chars": 1199,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/connectors/mail.js",
    "chars": 5083,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/connectors/memory.js",
    "chars": 898,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/current-context.js",
    "chars": 1194,
    "preview": "// Copyright IBM Corp. 2016,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/globalize.js",
    "chars": 376,
    "preview": "// Copyright IBM Corp. 2016,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/loopback.js",
    "chars": 9788,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/model.js",
    "chars": 37841,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/persisted-model.js",
    "chars": 71046,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/registry.js",
    "chars": 12113,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/runtime.js",
    "chars": 686,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/server-app.js",
    "chars": 9667,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "lib/utils.js",
    "chars": 4379,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "package.json",
    "chars": 3243,
    "preview": "{\n  \"name\": \"loopback\",\n  \"version\": \"3.28.0\",\n  \"description\": \"LoopBack: Open Source Framework for Node.js\",\n  \"homepa"
  },
  {
    "path": "server/middleware/context.js",
    "chars": 475,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "server/middleware/error-handler.js",
    "chars": 377,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "server/middleware/favicon.js",
    "chars": 489,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "server/middleware/rest.js",
    "chars": 1844,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "server/middleware/static.js",
    "chars": 609,
    "preview": "// Copyright IBM Corp. 2014,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "server/middleware/status.js",
    "chars": 778,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "server/middleware/token.js",
    "chars": 5599,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "server/middleware/url-not-found.js",
    "chars": 734,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "templates/reset-form.ejs",
    "chars": 18,
    "preview": "<form>\n  \n</form>\n"
  },
  {
    "path": "templates/verify.ejs",
    "chars": 176,
    "preview": "<h1>Thank You</h1>\n\n<p>\n  Thanks for registering. Please follow the link below to complete your registration.\n</p>\n\n<p>\n"
  },
  {
    "path": "test/access-control.integration.js",
    "chars": 15004,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/access-token.test.js",
    "chars": 24672,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/acl.test.js",
    "chars": 22975,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/app.test.js",
    "chars": 36088,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/authorization-scopes.test.js",
    "chars": 4023,
    "preview": "// Copyright IBM Corp. 2017,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/change-stream.test.js",
    "chars": 4535,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/change.test.js",
    "chars": 18350,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/checkpoint.test.js",
    "chars": 3220,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/context-options.test.js",
    "chars": 12519,
    "preview": "// Copyright IBM Corp. 2016,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/data-source.test.js",
    "chars": 5128,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/e2e/remote-connector.e2e.js",
    "chars": 1107,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/e2e/replication.e2e.js",
    "chars": 1253,
    "preview": "// Copyright IBM Corp. 2014,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/email.test.js",
    "chars": 2788,
    "preview": "// Copyright IBM Corp. 2013,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/error-handler.test.js",
    "chars": 601,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/access-control/common/models/access-token.json",
    "chars": 484,
    "preview": "{\n  \"name\": \"accessToken\",\n  \"base\": \"AccessToken\",\n  \"baseUrl\": \"access-tokens\",\n  \"acls\": [\n    {\n      \"accessType\": "
  },
  {
    "path": "test/fixtures/access-control/common/models/account.json",
    "chars": 886,
    "preview": "{\n  \"name\": \"accountWithReplaceOnPUTtrue\",\n  \"plural\": \"accounts-replacing\",\n  \"relations\": {\n    \"transactions\": {\n    "
  },
  {
    "path": "test/fixtures/access-control/common/models/accountWithReplaceOnPUTfalse.json",
    "chars": 887,
    "preview": "{\n  \"name\": \"accountWithReplaceOnPUTfalse\",\n  \"plural\": \"accounts-updating\",\n  \"relations\": {\n    \"transactions\": {\n    "
  },
  {
    "path": "test/fixtures/access-control/common/models/alert.json",
    "chars": 191,
    "preview": "{\n  \"name\": \"alert\",\n  \"acls\": [\n    {\n      \"accessType\": \"WRITE\",\n      \"permission\": \"DENY\",\n      \"principalType\": \""
  },
  {
    "path": "test/fixtures/access-control/common/models/bank.json",
    "chars": 624,
    "preview": "{\n  \"name\": \"bank\",\n  \"relations\": {\n    \"users\": {\n      \"model\": \"user\",\n      \"type\": \"hasMany\"\n    },\n    \"accounts\""
  },
  {
    "path": "test/fixtures/access-control/common/models/email.json",
    "chars": 186,
    "preview": "{\n  \"name\": \"email\",\n  \"base\": \"Email\",\n  \"acls\": [\n    {\n      \"accessType\": \"*\",\n      \"permission\": \"DENY\",\n      \"pr"
  },
  {
    "path": "test/fixtures/access-control/common/models/transaction.json",
    "chars": 193,
    "preview": "{\n  \"name\": \"transaction\",\n  \"acls\": [\n    {\n      \"accessType\": \"*\",\n      \"permission\": \"DENY\",\n      \"principalType\":"
  },
  {
    "path": "test/fixtures/access-control/common/models/user.json",
    "chars": 427,
    "preview": "{\n  \"name\": \"user\",\n  \"base\": \"User\",\n  \"relations\": {\n    \"accessTokens\": {\n      \"model\": \"accessToken\",\n      \"type\":"
  },
  {
    "path": "test/fixtures/access-control/server/config.json",
    "chars": 129,
    "preview": "{\n  \"port\": 3000,\n  \"host\": \"0.0.0.0\",\n  \"remoting\": {\n    \"errorHandler\": {\n      \"debug\": true,\n      \"log\": false\n   "
  },
  {
    "path": "test/fixtures/access-control/server/datasources.json",
    "chars": 84,
    "preview": "{\n  \"db\": {\n    \"connector\": \"memory\"\n  },\n  \"mail\": {\n    \"connector\": \"mail\"\n  }\n}"
  },
  {
    "path": "test/fixtures/access-control/server/model-config.json",
    "chars": 857,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"../common/models\",\n      \"./models\",\n      \"../../../../common/models\"\n    ]\n  },"
  },
  {
    "path": "test/fixtures/access-control/server/server.js",
    "chars": 654,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/e2e/server/models.js",
    "chars": 391,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/e2e/server/server.js",
    "chars": 679,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/both-configs-set/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/both-configs-set/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/both-configs-set/server/config.json",
    "chars": 461,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/both-configs-set/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/both-configs-set/server/model-config.json",
    "chars": 757,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/both-configs-set/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-false/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-false/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/config-default-false/server/config.json",
    "chars": 435,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-false/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-false/server/model-config.json",
    "chars": 638,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/config-default-false/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-true/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-true/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/config-default-true/server/config.json",
    "chars": 434,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-true/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/config-default-true/server/model-config.json",
    "chars": 638,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/config-default-true/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-false/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-false/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-false/server/config.json",
    "chars": 438,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-false/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-false/server/model-config.json",
    "chars": 638,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-false/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-true/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-true/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-true/server/config.json",
    "chars": 437,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-true/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-true/server/model-config.json",
    "chars": 638,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/config-defined-true/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-false/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-false/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-false/server/config.json",
    "chars": 388,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-false/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-false/server/model-config.json",
    "chars": 748,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-false/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-true/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-true/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-true/server/config.json",
    "chars": 388,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-true/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-true/server/model-config.json",
    "chars": 747,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/model-config-default-true/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-false/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-false/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-false/server/config.json",
    "chars": 388,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-false/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-false/server/model-config.json",
    "chars": 751,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-false/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-true/common/models/todo.js",
    "chars": 246,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-true/common/models/todo.json",
    "chars": 283,
    "preview": "{\n  \"name\": \"Todo\",\n  \"base\": \"PersistedModel\",\n  \"idInjection\": true,\n  \"options\": {\n    \"validateUpsert\": true\n  },\n  "
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-true/server/config.json",
    "chars": 388,
    "preview": "{\n  \"restApiRoot\": \"/api\",\n  \"host\": \"0.0.0.0\",\n  \"port\": 3000,\n  \"remoting\": {\n    \"rest\": {\n      \"normalizeHttpPath\":"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-true/server/datasources.json",
    "chars": 62,
    "preview": "{\n  \"db\": {\n    \"name\": \"db\",\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-true/server/model-config.json",
    "chars": 750,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"loopback/common/models\",\n      \"loopback/server/models\",\n      \"../common/models\""
  },
  {
    "path": "test/fixtures/shared-methods/model-config-defined-true/server/server.js",
    "chars": 387,
    "preview": "// Copyright IBM Corp. 2015,2019. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/simple-app/boot/bad.txt",
    "chars": 23,
    "preview": "this is not a js file!\n"
  },
  {
    "path": "test/fixtures/simple-app/boot/foo.js",
    "chars": 236,
    "preview": "// Copyright IBM Corp. 2013,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/simple-app/common/models/bar.js",
    "chars": 274,
    "preview": "// Copyright IBM Corp. 2015,2018. All Rights Reserved.\n// Node module: loopback\n// This file is licensed under the MIT L"
  },
  {
    "path": "test/fixtures/simple-app/common/models/bar.json",
    "chars": 39,
    "preview": "{\n  \"name\": \"bar\",\n  \"properties\": {}\n}"
  },
  {
    "path": "test/fixtures/simple-app/common/models/foo.json",
    "chars": 39,
    "preview": "{\n  \"name\": \"foo\",\n  \"properties\": {}\n}"
  },
  {
    "path": "test/fixtures/simple-app/server/config.json",
    "chars": 131,
    "preview": "{\n  \"port\": 3000,\n  \"host\": \"127.0.0.1\",\n  \"remoting\": {\n    \"errorHandler\": {\n      \"debug\": true,\n      \"log\": false\n "
  },
  {
    "path": "test/fixtures/simple-app/server/datasources.json",
    "chars": 44,
    "preview": "{\n  \"db\": {\n    \"connector\": \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/simple-app/server/model-config.json",
    "chars": 479,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"../common/models\",\n      \"./models\",\n      \"../../../../common/models\"\n    ]\n  },"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/access-token.json",
    "chars": 52,
    "preview": "{\n  \"name\": \"accessToken\",\n  \"base\": \"AccessToken\"\n}"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/appointment.json",
    "chars": 357,
    "preview": "{\n  \"name\": \"appointment\",\n  \"properties\": {\n    \"date\": \"date\"\n  },\n  \"options\": {\n    \"relations\": {\n      \"physician\""
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/customer-forceid.json",
    "chars": 174,
    "preview": "{\n  \"name\": \"customerforceidfalse\",\n  \"base\": \"PersistedModel\",\n  \"forceId\": false,\n  \"properties\": {\n    \"name\": {\n    "
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/customer.json",
    "chars": 236,
    "preview": "{\n  \"name\": \"customer\",\n  \"base\": \"PersistedModel\",\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"requi"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/email.json",
    "chars": 186,
    "preview": "{\n  \"name\": \"email\",\n  \"base\": \"Email\",\n  \"acls\": [\n    {\n      \"accessType\": \"*\",\n      \"permission\": \"DENY\",\n      \"pr"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/patient.json",
    "chars": 269,
    "preview": "{\n  \"name\": \"patient\",\n  \"properties\": {\n    \"name\": \"string\"\n  },\n  \"options\": {\n    \"relations\": {\n      \"physicians\":"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/physician.json",
    "chars": 231,
    "preview": "{\n  \"name\": \"physician\",\n  \"properties\": {\n    \"name\": \"string\"\n  },\n  \"relations\": {\n    \"patients\": {\n      \"model\": \""
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/profile.json",
    "chars": 119,
    "preview": "{\n  \"name\": \"profile\",\n  \"base\": \"PersistedModel\",\n  \"properties\": {\n    \"points\": {\n      \"type\": \"number\"\n    }\n  }\n}"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/store-replacing.json",
    "chars": 306,
    "preview": "{\n  \"name\": \"storeWithReplaceOnPUTtrue\",\n  \"plural\": \"stores-replacing\",\n  \"properties\": {},\n  \"scopes\": {\n    \"superSto"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/store-updating.json",
    "chars": 307,
    "preview": "{\n  \"name\": \"storeWithReplaceOnPUTfalse\",\n  \"plural\": \"stores-updating\",\n  \"properties\": {},\n  \"scopes\": {\n    \"superSto"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/store.json",
    "chars": 230,
    "preview": "{\n  \"name\": \"store\",\n  \"properties\": {},\n  \"scopes\": {\n    \"superStores\": {\n      \"where\": {\n        \"size\": \"super\"\n   "
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/user.json",
    "chars": 172,
    "preview": "{\n  \"name\": \"user\",\n  \"base\": \"User\",\n  \"relations\": {\n    \"accessTokens\": {\n      \"model\": \"accessToken\",\n      \"type\":"
  },
  {
    "path": "test/fixtures/simple-integration-app/common/models/widget.json",
    "chars": 220,
    "preview": "{\n  \"name\": \"widget\",\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"default\": \"DefaultWidgetName\"\n    }"
  },
  {
    "path": "test/fixtures/simple-integration-app/server/config.json",
    "chars": 300,
    "preview": "{\n  \"port\": 3000,\n  \"host\": \"0.0.0.0\",\n  \"cookieSecret\": \"2d13a01d-44fb-455c-80cb-db9cb3cd3cd0\",\n  \"remoting\": {\n    \"js"
  },
  {
    "path": "test/fixtures/simple-integration-app/server/datasources.json",
    "chars": 84,
    "preview": "{\n  \"db\": {\n    \"connector\": \"memory\"\n  },\n  \"mail\": {\n    \"connector\": \"mail\"\n  }\n}"
  },
  {
    "path": "test/fixtures/simple-integration-app/server/model-config.json",
    "chars": 1186,
    "preview": "{\n  \"_meta\": {\n    \"sources\": [\n      \"../common/models\",\n      \"./models\",\n      \"../../../../common/models\"\n    ]\n  },"
  }
]

// ... and 42 more files (download for full content)

About this extraction

This page contains the full source code of the strongloop/loopback GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 242 files (1.2 MB), approximately 334.1k tokens, and a symbol index with 299 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!