Repository: Netflix/falcor Branch: master Commit: 39d64776cf9d Files: 367 Total size: 4.5 MB Directory structure: gitextract_2ph5kury/ ├── .bithoundrc ├── .editorconfig ├── .eslintrc ├── .github/ │ └── workflows/ │ ├── ci.yml │ └── docs.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── MIGRATIONS.md ├── OSSMETADATA ├── README.md ├── all.js ├── authors.txt ├── bower.json ├── browser.js ├── build/ │ ├── deploy-ghpages.sh │ ├── falcor-jsdoc-template/ │ │ ├── README.md │ │ ├── publish.js │ │ └── tmpl/ │ │ ├── augments.tmpl │ │ ├── container.tmpl │ │ ├── details.tmpl │ │ ├── example.tmpl │ │ ├── examples.tmpl │ │ ├── exceptions.tmpl │ │ ├── layout.tmpl │ │ ├── mainpage.tmpl │ │ ├── members.tmpl │ │ ├── method.tmpl │ │ ├── navigation-subgroup.tmpl │ │ ├── navigation.tmpl │ │ ├── params.tmpl │ │ ├── properties.tmpl │ │ ├── returns.tmpl │ │ ├── source.tmpl │ │ ├── tutorial.tmpl │ │ └── type.tmpl │ ├── gulp-build.js │ ├── gulp-clean.js │ ├── gulp-perf.js │ └── jsdoc.json ├── conf.json ├── deployKey.enc ├── dist/ │ ├── falcor.all.js │ └── falcor.browser.js ├── doc/ │ ├── DataSource.html │ ├── FromEsObserverAdapter.html │ ├── Model.html │ ├── Model.js.html │ ├── ModelResponse.html │ ├── ModelResponseObserver.html │ ├── Observable.html │ ├── Subscription.html │ ├── ToEsSubscriptionAdapter.html │ ├── get_getCache.js.html │ ├── global.html │ ├── index.html │ ├── index.js.html │ ├── internal_index.js.html │ ├── invalidate_invalidatePathMaps.js.html │ ├── invalidate_invalidatePathSets.js.html │ ├── request_GetRequestV2.js.html │ ├── request_RequestQueueV2.js.html │ ├── request_complement.js.html │ ├── response_AssignableDisposable.js.html │ ├── response_ModelResponse.js.html │ ├── response_ModelResponseObserver.js.html │ ├── response_get_GetResponse.js.html │ ├── response_set_SetResponse.js.html │ ├── response_set_setGroupsIntoCache.js.html │ ├── response_set_setRequestCycle.js.html │ ├── set_setJSONGraphs.js.html │ ├── set_setPathMaps.js.html │ ├── set_setPathValues.js.html │ ├── support_reconstructPath.js.html │ ├── toEsObservable.js.html │ ├── typedefs_Atom.js.html │ ├── typedefs_DataSource.js.html │ ├── typedefs_JSONEnvelope.js.html │ ├── typedefs_JSONGraph.js.html │ ├── typedefs_JSONGraphEnvelope.js.html │ ├── typedefs_Key.js.html │ ├── typedefs_KeySet.js.html │ ├── typedefs_Observable.js.html │ ├── typedefs_Path.js.html │ ├── typedefs_PathSet.js.html │ ├── typedefs_PathValue.js.html │ └── typedefs_Range.js.html ├── examples/ │ └── datasource/ │ └── webWorkerSource.js ├── gulpfile.js ├── lib/ │ ├── Model.js │ ├── ModelDataSourceAdapter.js │ ├── ModelRoot.js │ ├── deref/ │ │ ├── hasValidParentReference.js │ │ ├── index.js │ │ └── sync.js │ ├── errors/ │ │ ├── BoundJSONGraphModelError.js │ │ ├── InvalidDerefInputError.js │ │ ├── InvalidModelError.js │ │ ├── InvalidSourceError.js │ │ ├── MaxRetryExceededError.js │ │ ├── NullInPathError.js │ │ └── applyErrorPrototype.js │ ├── get/ │ │ ├── followReference.js │ │ ├── get.js │ │ ├── getBoundValue.js │ │ ├── getCache.js │ │ ├── getCachePosition.js │ │ ├── getValue.js │ │ ├── getValueSync.js │ │ ├── getVersion.js │ │ ├── index.js │ │ ├── onError.js │ │ ├── onMissing.js │ │ ├── onValue.js │ │ ├── onValueType.js │ │ ├── sync.js │ │ ├── util/ │ │ │ ├── clone.js │ │ │ ├── isExpired.js │ │ │ ├── isMaterialzed.js │ │ │ └── isPathValue.js │ │ └── walkPath.js │ ├── index.js │ ├── internal/ │ │ ├── absolutePath.js │ │ ├── context.js │ │ ├── head.js │ │ ├── index.js │ │ ├── invalidated.js │ │ ├── key.js │ │ ├── model-created.js │ │ ├── next.js │ │ ├── parent.js │ │ ├── path.js │ │ ├── prev.js │ │ ├── privatePrefix.js │ │ ├── ref-index.js │ │ ├── ref.js │ │ ├── refs-length.js │ │ ├── reservedPrefix.js │ │ ├── tail.js │ │ └── version.js │ ├── invalidate/ │ │ ├── invalidatePathMaps.js │ │ └── invalidatePathSets.js │ ├── lru/ │ │ ├── collect.js │ │ ├── promote.js │ │ └── splice.js │ ├── request/ │ │ ├── GetRequestV2.js │ │ ├── RequestQueueV2.js │ │ ├── RequestTypes.js │ │ ├── complement.js │ │ ├── flushGetRequest.js │ │ └── sendSetRequest.js │ ├── response/ │ │ ├── AssignableDisposable.js │ │ ├── CallResponse.js │ │ ├── InvalidateResponse.js │ │ ├── ModelResponse.js │ │ ├── ModelResponseObserver.js │ │ ├── get/ │ │ │ ├── GetResponse.js │ │ │ ├── checkCacheAndReport.js │ │ │ ├── getRequestCycle.js │ │ │ ├── getWithPaths.js │ │ │ ├── index.js │ │ │ └── validInput.js │ │ └── set/ │ │ ├── SetResponse.js │ │ ├── index.js │ │ ├── setGroupsIntoCache.js │ │ ├── setRequestCycle.js │ │ └── setValidInput.js │ ├── schedulers/ │ │ ├── ASAPScheduler.js │ │ ├── ImmediateScheduler.js │ │ └── TimeoutScheduler.js │ ├── set/ │ │ ├── index.js │ │ ├── setJSONGraphs.js │ │ ├── setPathMaps.js │ │ ├── setPathValues.js │ │ ├── setValue.js │ │ └── sync.js │ ├── support/ │ │ ├── array-flat-map.js │ │ ├── clone.js │ │ ├── createHardlink.js │ │ ├── currentCacheVersion.js │ │ ├── expireNode.js │ │ ├── getExpires.js │ │ ├── getSize.js │ │ ├── getTimestamp.js │ │ ├── getType.js │ │ ├── hasOwn.js │ │ ├── identity.js │ │ ├── incrementVersion.js │ │ ├── insertNode.js │ │ ├── isAlreadyExpired.js │ │ ├── isExpired.js │ │ ├── isFunction.js │ │ ├── isInternalKey.js │ │ ├── isJSONEnvelope.js │ │ ├── isJSONGraphEnvelope.js │ │ ├── isObject.js │ │ ├── isPathInvalidation.js │ │ ├── isPathValue.js │ │ ├── isPrimitive.js │ │ ├── mergeJSONGraphNode.js │ │ ├── mergeValueOrInsertBranch.js │ │ ├── noop.js │ │ ├── now.js │ │ ├── reconstructPath.js │ │ ├── removeNode.js │ │ ├── removeNodeAndDescendants.js │ │ ├── replaceNode.js │ │ ├── transferBackReferences.js │ │ ├── unlinkBackReferences.js │ │ ├── unlinkForwardReference.js │ │ ├── updateBackReferenceVersions.js │ │ ├── updateNodeAncestors.js │ │ ├── validateInput.js │ │ └── wrapNode.js │ ├── toEsObservable.js │ ├── typedefs/ │ │ ├── Atom.js │ │ ├── DataSource.js │ │ ├── JSONEnvelope.js │ │ ├── JSONGraph.js │ │ ├── JSONGraphEnvelope.js │ │ ├── Key.js │ │ ├── KeySet.js │ │ ├── Observable.js │ │ ├── Path.js │ │ ├── PathSet.js │ │ ├── PathValue.js │ │ └── Range.js │ ├── types/ │ │ ├── atom.js │ │ ├── error.js │ │ └── ref.js │ └── values/ │ ├── expires-never.js │ └── expires-now.js ├── package.json ├── performance/ │ ├── README.md │ ├── TriggerDataSource.js │ ├── browser.js │ ├── device.js │ ├── formatter/ │ │ ├── CSVFormatter.js │ │ └── CSVTransform.js │ ├── karma.conf.js │ ├── models/ │ │ ├── index.js │ │ └── legacy/ │ │ ├── _.js │ │ ├── _Cache.js │ │ ├── index.js │ │ └── macro/ │ │ └── _Falcor.js │ ├── node.js │ ├── reporters/ │ │ ├── browserTestReporter.js │ │ ├── karmaBenchmarkCSVReporter.js │ │ └── nodeTestReporter.js │ ├── scripts/ │ │ └── transformCSV.js │ ├── testRunner.js │ └── tests/ │ ├── clone/ │ │ └── clone.perf.js │ ├── deref/ │ │ └── index.js │ ├── get/ │ │ ├── get.core.perf.js │ │ ├── get.perf.js │ │ ├── onValue.perf.js │ │ └── walk.perf.js │ ├── inMemoryCache.js │ ├── lru/ │ │ └── index.js │ ├── request/ │ │ └── request-queue.js │ ├── set/ │ │ └── set.json-graph.perf.js │ ├── standard.js │ ├── testMerge.js │ └── testSuite.js ├── server.js ├── test/ │ ├── .eslintrc.yaml │ ├── CacheGenerator.js │ ├── Model.spec.js │ ├── cleanData.js │ ├── data/ │ │ ├── Cache.js │ │ ├── ErrorDataSource.js │ │ ├── LocalDataSource.js │ │ ├── ReducedCache.js │ │ ├── asyncifyDataSource.js │ │ └── expected/ │ │ ├── Bound.js │ │ ├── Complex.js │ │ ├── References.js │ │ ├── Values.js │ │ └── index.js │ ├── falcor/ │ │ ├── call/ │ │ │ └── call.spec.js │ │ ├── deref/ │ │ │ ├── deref.errors.spec.js │ │ │ ├── deref.hasValidParentReference.spec.js │ │ │ └── deref.spec.js │ │ ├── error/ │ │ │ └── error.spec.js │ │ ├── get/ │ │ │ ├── get.cache-only.spec.js │ │ │ ├── get.cacheAsDataSource.spec.js │ │ │ ├── get.clone.spec.js │ │ │ ├── get.dataSource-and-bind.spec.js │ │ │ ├── get.dataSource-and-cache.spec.js │ │ │ ├── get.dataSource-only.spec.js │ │ │ ├── get.gen.spec.js │ │ │ ├── get.model.adapter.spec.js │ │ │ └── get.pathSyntax.spec.js │ │ ├── invalidate/ │ │ │ ├── invalidate.cache-only.spec.js │ │ │ └── invalidate.change-handler.spec.js │ │ ├── operations.spec.js │ │ ├── schedulers/ │ │ │ └── schedulers.spec.js │ │ └── set/ │ │ ├── set.cache-only.spec.js │ │ ├── set.cacheAsDataSource-and-cache.spec.js │ │ ├── set.change-handler.spec.js │ │ ├── set.dataSource-and-bind.spec.js │ │ ├── set.dataSource-and-cache.spec.js │ │ ├── set.dataSource-only.spec.js │ │ ├── set.pathSyntax.spec.js │ │ └── set.setCache.spec.js │ ├── get-core/ │ │ ├── deref.spec.js │ │ ├── edges.spec.js │ │ ├── errors.spec.js │ │ ├── get.cache.spec.js │ │ ├── missing.spec.js │ │ ├── null.spec.js │ │ ├── references.spec.js │ │ └── values.spec.js │ ├── getCoreRunner.js │ ├── getTestRunner.js │ ├── hardlink/ │ │ ├── hardlink.add.spec.js │ │ └── hardlink.remove.spec.js │ ├── integration/ │ │ ├── call.spec.js │ │ ├── dedupe.spec.js │ │ ├── express.spec.js │ │ └── get.spec.js │ ├── internal/ │ │ ├── ModelRoot.comparator.spec.js │ │ ├── request/ │ │ │ ├── GetRequest.add.spec.js │ │ │ ├── GetRequest.batch.spec.js │ │ │ ├── GetRequest.spec.js │ │ │ ├── RequestQueue.get.spec.js │ │ │ └── complement.spec.js │ │ └── sync/ │ │ └── _setValueSync.spec.js │ ├── invalidate/ │ │ ├── invalidate.spec.js │ │ ├── pathMaps.spec.js │ │ └── pathSets.spec.js │ ├── isAssertionError.js │ ├── lru/ │ │ ├── lru.promote.get.spec.js │ │ ├── lru.promote.set.spec.js │ │ ├── lru.splice.expired.spec.js │ │ └── lru.splice.overwrite.spec.js │ ├── outputGenerator.js │ ├── response/ │ │ └── ModelResponseObserver.spec.js │ ├── set/ │ │ ├── edge-cases.spec.js │ │ ├── jsonGraphs/ │ │ │ ├── atom.spec.js │ │ │ ├── branch.spec.js │ │ │ ├── expired.spec.js │ │ │ ├── primitive.spec.js │ │ │ └── reference.spec.js │ │ ├── pathMaps/ │ │ │ ├── atom.spec.js │ │ │ ├── branch.spec.js │ │ │ ├── expired.spec.js │ │ │ └── primitive.spec.js │ │ ├── pathValues/ │ │ │ ├── atom.spec.js │ │ │ ├── branch.spec.js │ │ │ ├── expired.spec.js │ │ │ └── primitive.spec.js │ │ └── support/ │ │ ├── getModel.js │ │ ├── jsonGraph.js │ │ ├── jsonGraphEnvelope.js │ │ ├── partial-cache.js │ │ ├── pathMap.js │ │ ├── pathMapEnvelope.js │ │ ├── strip.js │ │ └── whole-cache.js │ ├── testRunner.js │ ├── toObs.js │ └── zipSpy.js └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bithoundrc ================================================ { "ignore": [ "**/node_modules/**", "build/**", "coverage/**", "dist/**", "macros/**", "performance/**", "test/**", "gulpfile.js", "server.js" ] } ================================================ FILE: .editorconfig ================================================ # http://editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 4 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.md] insert_final_newline = false trim_trailing_whitespace = false ================================================ FILE: .eslintrc ================================================ { "env": { "browser": false, "node": true, "es6": false }, "rules": { // possible errors "comma-dangle": [ 2, "only-multiline" ], "no-console": [ 2 ], "no-constant-condition": [ 2 ], "no-control-regex": [ 2 ], "no-debugger": [ 2 ], "no-dupe-args": [ 2 ], "no-dupe-keys": [ 2 ], "no-duplicate-case": [ 2 ], "no-empty": [ 2 ], "no-empty-character-class": [ 2 ], "no-ex-assign": [ 2 ], "no-extra-boolean-cast": [ 2 ], "no-cond-assign": [ 2, "always" ], "no-extra-semi": [ 2 ], "no-func-assign": [ 2 ], // this is for variable hoisting, not necessary if we use block scoped declarations // "no-inner-declarations": [ 2, "both" ], "no-invalid-regexp": [ 2 ], "no-irregular-whitespace": [ 2 ], "no-negated-in-lhs": [ 2 ], // when IE8 dies "no-reserved-keys": [ 0 ], "no-regex-spaces": [ 2 ], "no-sparse-arrays": [ 2 ], "no-unreachable": [ 2 ], "use-isnan": [ 2 ], // should we enforce valid documentation comments? // i.e., if you do documentation, do it right // "valid-jsdoc": [ 2 ], "valid-typeof": [ 2 ], // best practices "block-scoped-var": [ 2 ], "consistent-return": [ 2 ], "curly": [ 2 ], "default-case": [ 2 ], "dot-notation": [ 2, { "allowKeywords": true } ], "eqeqeq": [ 2, "smart" ], "guard-for-in": [ 2 ], "no-alert": [ 2 ], "no-caller": [ 2 ], "no-div-regex": [ 2 ], "no-eq-null": [ 0 ], "no-eval": [ 2 ], "no-extend-native": [ 2 ], "no-extra-bind": [ 2 ], "no-fallthrough": [ 2 ], "no-floating-decimal": [ 2 ], "no-implied-eval": [ 2 ], "no-iterator": [ 2 ], "no-labels": [ 0 ], "no-lone-blocks": [ 2 ], "no-loop-func": [ 2 ], "no-multi-spaces": [ 2 ], "no-native-reassign": [ 2 ], "no-new": [ 2 ], "no-new-func": [ 2 ], "no-new-wrappers": [ 2 ], "no-octal": [ 2 ], "no-octal-escape": [ 2 ], "no-param-reassign": [ 0 ], "no-proto": [ 2 ], "no-process-env": [ 2 ], "no-redeclare": [ 2 ], "no-return-assign": [ 2 ], "no-script-url": [ 2 ], "no-self-compare": [ 2 ], "no-sequences": [ 2 ], "no-throw-literal": [ 2 ], "no-unused-expressions": [ 2 ], "no-warning-comments": [ 0 ], "no-with": [ 2 ], "radix": [ 2 ], "wrap-iife": [ 2 ], "yoda": [ 0 ], // strict mode /* "strict": [ 2, "global" ], */ // variables "no-catch-shadow": [ 2 ], "no-delete-var": [ 2 ], "no-shadow": [ 2 ], "no-shadow-restricted-names": [ 2 ], "no-undef": [ 2 ], "no-undef-init": [ 2 ], "no-undefined": [ 0 ], "no-unused-vars": [ 2, { "vars": "all", "args": "none" } ], "no-use-before-define": [ 2, "nofunc" ], // node.js "handle-callback-err": [ 2, "^.*(e|E)rr" ], "no-mixed-requires": [ 2 ], "no-new-require": [ 2 ], "no-path-concat": [ 2 ], "no-process-exit": [ 0 ], // ES6 "generator-star-spacing": [ 2, "after" ], // stylistic "block-spacing": [ "error", "always" ], // lotsa Stroustrup for else-blocks // "brace-style": [ "error", "1tbs", { "allowSingleLine": true } ], "comma-spacing": [ "error" ], "comma-style": [ "error" ], "quote-props": [ "error", "as-needed" ], "camelcase": [ 1, { "properties": "always" } ], "eol-last": [ 2 ], "key-spacing": [ 2 ], "keyword-spacing": [ 2 ], "no-lonely-if": [ 2 ], "no-array-constructor": [ 2 ], "no-extra-parens": [ 2, "functions" ], "no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ], "no-nested-ternary": [ 2 ], "no-new-object": [ 2 ], "no-underscore-dangle": [ 0 ], "no-trailing-spaces": [ 2 ], "semi": [ 2, "always" ], "space-before-blocks": [ 2, "always" ], "space-before-function-paren": [ 2, { "anonymous": "never", "named": "never" } ], "space-infix-ops": [ 2 ], "space-unary-ops": [ 2, { "words": true, "nonwords": false } ], "spaced-comment": [ 2, "always", { "exceptions": [ "-", "+" ] } ], "quotes": [ 2, "double", "avoid-escape" ], "wrap-regex": [ 2 ] } } ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 10.x - name: Install dependencies run: npm install - name: Run eslint run: npx eslint *.js lib/**/*.js - name: Run tests run: npm test - name: Run test coverage report uses: coverallsapp/github-action@master with: github-token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/docs.yml ================================================ name: Docs on: push: branches: [ master ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 10.x - name: Install dependencies run: npm install - name: Generate docs run: npm run doc - name: Publish docs to gh-pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./doc destination_dir: doc keep_files: true enable_jekyll: true ================================================ FILE: .gitignore ================================================ node_modules .DS_Store bin/ bin2 out/ tmp/ performance/bin/ performance/device/ !tasks/bin coverage .idea *.iml *.tar.gz cTestFinal.js npm-debug.log .vscode ================================================ FILE: .npmignore ================================================ test/ .DS_Store bin/ bin2 out/ tmp/ performance/bin/ performance/device/ !tasks/bin coverage .idea *.iml *.tar.gz cTestFinal.js npm-debug.log .vscode ================================================ FILE: .npmrc ================================================ registry=https://registry.npmjs.com ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "10" branches: only: - master - 0.x - 1.0.0 # Set up keys to allow pushes, and update npm to the latest version before_install: npm update -g npm # travis runs 'npm install' by default, as the 'install' step before_script: npm run dist; # travis runs 'npm test' by default, as the 'script' step # PLACEHOLDER FOR COVERAGE REPORTS ON GITHUB, IF WE WANT THEM. # ALSO NEED TO ADD .coveralls.yml, IF WE WANT TO USE IT ON THE PRIVATE # REPO. after_script: - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js - npm run deploy-ghpages notifications: email: falcor@netflix.com ================================================ FILE: CHANGELOG.md ================================================ # 2.4.1 ## Bugfix - Republished to remove an issue with a hash generation of 2.4.0 # 2.4.0 ## Changes - [Handling fromPath errors in SetResponse constructor](https://github.com/Netflix/falcor/pull/989) # 2.3.2 ## Bugfix - [Handling fromPath errors in SetResponse constructor](https://github.com/Netflix/falcor/pull/989) # 2.3.1 ## Bugfix - [Avoid fetching null paths](https://github.com/Netflix/falcor/pull/984) # 2.3.0 ## Changes - [Skip nulls in path instead of throwing](https://github.com/Netflix/falcor/pull/982) # 2.2.2 ## Changes - Updated falcor-path-utils to pull in changes: [Use NOINLINE to avoid inlining function definitions](https://github.com/Netflix/falcor-path-utils/pull/23) - [Reduced allocation garbage](https://github.com/Netflix/falcor/pull/980) # 2.2.1 ## Bugs - [Empty key sets in path sets could result in an incomplete response](https://github.com/Netflix/falcor/pull/979) # 2.2.0 ## Features - [Request attempt count passed down to DataSource get/set](https://github.com/Netflix/falcor/pull/970) # 2.1.0 ## Features - [Added flags for potentially expensive algorithms](https://github.com/Netflix/falcor/pull/962) ## Other - [Unit tests converted to Jest](https://github.com/Netflix/falcor/pull/961) # 2.0.7 ## Bugs - [Correct module.export usage](https://github.com/Netflix/falcor/pull/960) # 2.0.6 ## Bugs - [Fix handling of empty keysets](https://github.com/Netflix/falcor/pull/955) # 2.0.5 ## Bugs - [Fix request path deduplication](https://github.com/Netflix/falcor/pull/949) # 2.0.4 ## Bugs - [Make Model#_clone return instance of current type](https://github.com/Netflix/falcor/pull/730) - [Fix error stack traces](https://github.com/Netflix/falcor/pull/941) - [Fix handling of outerResults as an optional argument](https://github.com/Netflix/falcor/pull/947) # 2.0.3 ## Bugs - [Fix model._setValueSync() internal API to set path bound values in cache](https://github.com/Netflix/falcor/pull/933) # 2.0.2 ## Bugs - [Fix model.get() dispose to cancel asynchronous DataSource request](https://github.com/Netflix/falcor/pull/933) - [Fix model.batch() losing maxRetries config](https://github.com/Netflix/falcor/pull/932) # 2.0.1 ## Bugs - [Fix build issues by replacing asap with falcor-asap](https://github.com/Netflix/falcor/pull/928) # 2.0.0 ## Features - [Dedupe requests partially with existing requests](https://github.com/Netflix/falcor/pull/897) - [Add missing paths information when raising MaxRetryExceededError](https://github.com/Netflix/falcor/pull/874) ## Bugs - [Fix "expires: 0" metadata on atoms](https://github.com/Netflix/falcor/pull/905/commits) - [Protect against model.invalidate from destroying cache](https://github.com/Netflix/falcor/pull/903) - [Fix retry count logic](https://github.com/Netflix/falcor/pull/904) # 1.1.0 ## Bugs - [Fix maxRetries on clone](https://github.com/Netflix/falcor/pull/917) - [Disables whole-branch response merging](https://github.com/Netflix/falcor/pull/920) - [Fix model.set claiming objects passed as argument](https://github.com/Netflix/falcor/pull/920) # 1.0.0 ## Features - [Allow errorSelector to change $type](https://github.com/Netflix/falcor/issues/828) - [Falcor.keys](https://github.com/Netflix/falcor/issues/708) - Adds a function to the namespace of `falcor` for ease of json key iteration. - [Remove Rx From Core](https://github.com/Netflix/falcor/issues/465) - [Remove Rx From Get](https://github.com/Netflix/falcor/issues/506) - [Remove Rx From Set](https://github.com/Netflix/falcor/issues/604) - [Add Falcor Build that contains Router](https://github.com/Netflix/falcor/issues/521) - [Code clean-up: Remove selector function / output PathValues code throughout code base](https://github.com/Netflix/falcor/issues/453) - [Remove asPathValues from ModelResponse](https://github.com/Netflix/falcor/issues/452) - [Improve deref for better MVC Integration](https://github.com/Netflix/falcor/issues/501) ## Bugs - [Webpack](https://github.com/Netflix/falcor/issues/586) - [MaxRetryExceededError when a route returns a null value not wrapped in an atom](https://github.com/Netflix/falcor/issues/535) - [The latest release throws model.get(...).then is not a function error](https://github.com/Netflix/falcor/issues/530) - [Collect does not adjust cache size.](https://github.com/Netflix/falcor/issues/507) - [number 0 becomes empty atom](https://github.com/Netflix/falcor/issues/460) - [\`this.clone()\` is undefined](https://github.com/Netflix/falcor/issues/442) - [Model#call() -> Error: "no method 'reduce' on 'localRoot.set(...).reduce'"](https://github.com/Netflix/falcor/issues/533) - [Observable and CompositeDisposable declared but not used](https://github.com/Netflix/falcor/issues/573) - [Path returned from falcor-router is undefined after call](https://github.com/Netflix/falcor/issues/589) - [New deref doesn't work when deref'ing to a reference.](https://github.com/Netflix/falcor/issues/559) - [New deref from an already deref'd model doesn't include the parent model's path.](https://github.com/Netflix/falcor/issues/560) # 0.1.15 ## Bugs - [Fix deref completing without onNext'ing if preload paths fail](https://github.com/Netflix/falcor/pull/667) - [Fix deref handling for paths that return an $atom of undefined](https://github.com/Netflix/falcor/pull/663) - [Make sure deref calls the DataSource, when starting from a broken reference](https://github.com/Netflix/falcor/pull/661) - [Fix expired reference handling, to return undefined, instead of InvalidModelError](https://github.com/Netflix/falcor/pull/658) - [Fixed expiry handling for getValueSync](https://github.com/Netflix/falcor/pull/651) - [Fixed distinct comparator to account for meta-data ($expires for example)](https://github.com/Netflix/falcor/pull/644) - [Fix getCache() serialization issues](https://github.com/Netflix/falcor/pull/640) - [Fixed reference promotion in LRU list](https://github.com/Netflix/falcor/pull/636) - [Fixed errorSelector bugs - cases where it wasn't invoked, and invoked with malformed paths](https://github.com/Netflix/falcor/pull/611) - [Fixed exceptions when call responses didn't contain any returned path/values](https://github.com/Netflix/falcor/pull/600) ================================================ FILE: LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You 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 the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You 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, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its 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. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2012 Netflix, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MIGRATIONS.md ================================================ # 1.x to 2.x Models always (usually) onNext ------- When starting with the following json graph ```javascript { lists: { 2343: { 0: { $type: "ref", value: ["videos", 123] }, 1: { $type: "ref", value: ["videos", 123] } } }, videos: { 123: { name: { $type: "atom", value: undefined } } } } ``` A `get` would not emit values for intermediate branches found in the cache, unless an atom was found ```javascript const json = await model.get(["lists", 2343, {to: 1}, "name"]); { json: { { lists: { 2343: { 0: {} } } } } } ``` Where now, any branches or references found in the cache will always be emitted in the json output ```javascript const json = await model.get(["lists", 2343, {to: 1}, "name"]); { json: { lists: { 2343: { 0: {}, 1: {} } } } } ``` That means that even requesting no paths will emit an empty object, as the cache root will be found ```javascript const json = await model.get(); { json: {} } ``` The only case where get won't onNext at least one value is when it receives only errors from the underlying data source. # 0.x to 1.x Refs no longer emitted as json ------- `get` no longer emits references as leaf values. When starting with the following json graph ```javascript { lists: { 2343: { 0: { $type: "ref", value: ["videos", 123] } } }, videos: { 123: { name: "House of cards" } } } ``` Previously the following would emit a ref ```javascript const json = await model.get(["lists", 2343, "0"]); { lists: { 2343: { 0: ["videos", 123] } } } ``` Where now, a key with undefined is emitted ```javascript const json = await model.get(["lists", 2343, "0"]); { lists: { 2343: { 0: undefined } } } ``` No More Rx ------- [Documentation on ModelResponse is found here](http://netflix.github.io/falcor/doc/ModelResponse.html) In 0.x `ModelResponse`'s prototype inherited from `Rx.Observable` in the following way. ```javascript var Rx = require('rx/dist/rx'); var ModelResponse = function ModelResponse(...) {...}; ModelResponse.prototype = Object.create(Rx.Observable.prototype); ... ``` This means that after a `get`, `set`, or `call` any `Rx` operator could be used. E.G. ```javascript model. get(['hello', 'falcor']). doAction(function(x) { ... }). flatMap(function(x) { return model.get(...); }). subscribe(...); ``` If your application relies on that behavior there are two possible upgrade paths. If your application does not rely on `Rx`, but only the `subscribe` from `Observable` then nothing has changed except for file size. #### Option 1 Alter the prototype for `get`, `set`, and `call` to return `Rx.Observables`. ```javascript var Rx = require('rx'); var falcor = require('./lib'); var Model = falcor.Model; var slice = Array.prototype.slice; var noRx = { get: Model.prototype.get, set: Model.prototype.set, call: Model.prototype.call }; Model.prototype.get = function getWithRx() { var args = slice.call(arguments, 0); return convertToRx(this, 'get', args); }; Model.prototype.set = function setWithRx() { var args = slice.call(arguments, 0); return convertToRx(this, 'set', args); }; Model.prototype.call = function callWithRx() { var args = slice.call(arguments, 0); return convertToRx(this, 'call', args); }; function convertToRx(model, method, args) { return Rx.Observable.create(function(observer) { return noRx[method].apply(model, args).subscribe(observer); }); } ``` ##### Pros * This upgrade only has to be done once and required once for the whole application to receive the benefits. ##### Cons * In the same vein, the whole application is forced into using the Rx based falcor whether it wants to or not since the prototype has been edited. #### Option 2 Wrap all calls to falcor with a Falcor.Subscribable -> Rx.Observable call. ```javascript var Rx = require('rx'); var Observable = Rx.Observable; module.exports = function toObservable(response) { return Observable.create(function(observer) { return response.subscribe(observer); }); }; ... // Don't forget to wrap the progressively() call as well. toObservable( model. get(['my', 'path']). progressively()). doAction(function() { // Something awesome goes here }). subscribe(); ``` ##### Pros * Its a more controlled approach since its opt-in only. ##### Cons * This has to be done everywhere a call to falcor is made and Rx is the desired output format. * can be a bit tedious :) Deref ------------- [Documentation on deref is found here](http://netflix.github.io/falcor/doc/Model.html#deref) `deref` has changed to use the output from a `ModelResponse` instead of specifying the destination via path and leaves. This means that there will be problems if you rely on `Object.keys` to iterate over your `json`. Instead, use `falcor.keys`. It will strip out the `$__path` from the `ModelResponse's` `json`. Lets create a model with some initial cache. ```javascript var model = new Model({ cache: { genreLists: { 0: Model.ref(['lists', 'A']) }, lists: { A: { 1337: Model.ref(['videos', 1337]) } }, videos: { 1337: { title: Model.atom('Total Recall (June 1st, 1990)') } } } }); ``` If we were to use `deref` in the **old** way we would have to perform the following to dereference to [genreLists, 0, 0]. ```javascript model. // Creates a dataSource (more than likely means a network call) call since // imageUrl does not exist in the cache. deref(['genreLists', 0, 0], ['title', 'imageUrl']). subscribe(function(boundModel) { // equivalent to model.get(['genreLists', 0, 0, 'title']) // -> { json: { title: 'Total Recall (June 1st, 1990)' } } boundModel.get(['title'])... // Other rendering stuff / application logic ... }); ``` ##### Cons * The knowledge of leaves were required. * Potential additional network requests could be made. * Always async. * Not very simple to explain how this works. The new `deref` works from the output of `ModelResponse`, so the same thing could be accomplished with the following. ```javascript model. get(['genreLists', 0, 0, 'title']). subscribe(function(x) { var json = x.json; var boundModel = model.deref(json.genreLists[0][0]); // equivalent to model.get(['genreLists', 0, 0, 'title']) // -> { json: { title: 'Total Recall (June 1st, 1990)' } } // If 'imageUrl' is used then a dataSource call would be made. boundModel.get(['title'])... // Other rendering stuff / application logic ... }); ``` ##### Pros * Simpler to grok/use * Promotes better application architecture. * Always synchronous. Promise shimming ---------------- In 0.x we depend on the 'promise' npm package to supply a Promise implementation on platforms missing the Promise builtin. In 1.x the choice of Promise shim is made at bundle build time. The supplied bundles are built with the same 'promise' npm package. To replicate this in your own Browserify build use the `insertGlobalVars` browserify option to use the Promise shim of your choice: ```javascript browserify(filename, { insertGlobalVars: { Promise: function (file, basedir) { return 'typeof Promise === "function" ? Promise : require("promise")'; } } } ``` With Webpack we can use `ProvidePlugin` to the same effect: ```javascript var path = require("path"); var webpack = require("webpack"); module.exports = { plugins: [ new webpack.ProvidePlugin({ Promise: path.join(__dirname, "promise-implementation"), }) ] }; ``` Where promise-implementation.js is: ```javascript module.exports = global.Promise || require("promise"); ``` For those not using Falcor's Promise functionality (i.e. `model.get().then(...)`) or deploying only to modern browsers, omitting the 'promise' package from your build will save ~1KB page weight from your build after gzipping and minification. ================================================ FILE: OSSMETADATA ================================================ osslifecycle=active ================================================ FILE: README.md ================================================

# Falcor [![Build Status](https://travis-ci.org/Netflix/falcor.svg)](https://travis-ci.org/Netflix/falcor) [![Coverage Status](https://coveralls.io/repos/Netflix/falcor/badge.svg?branch=master&service=github)](https://coveralls.io/github/Netflix/falcor?branch=master) ## 2.0 **2.0** is the current stable Falcor release. **0.x** and **1.x** users are welcome to upgrade. - [Breaking changes between **1.x** and **2.0**](https://github.com/Netflix/falcor/blob/master/MIGRATIONS.md). - [Breaking changes between **0.x** and **1.x**](https://github.com/Netflix/falcor/blob/1.0.0/MIGRATIONS.md). ## Roadmap Issues we're tracking as part of our roadmap are tagged with the [roadmap](https://github.com/Netflix/falcor/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap) label. They are split into [enhancement](https://github.com/Netflix/falcor/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap+label%3Aenhancement), [stability](https://github.com/Netflix/falcor/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap+label%3Astability), [performance](https://github.com/Netflix/falcor/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap+label%3Aperformance), [tooling](https://github.com/Netflix/falcor/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap+label%3Atooling), [infrastructure](https://github.com/Netflix/falcor/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap+label%3Ainfrastructure) and [documentation](https://github.com/Netflix/falcor/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap+label%3Adocumentation) categories, with near, medium and longer term labels to convey a broader sense of the order in which we plan to approach them. ## Getting Started You can check out [a working example server for Netflix-like application](https://github.com/netflix/falcor-express-demo) right now. Alternately, you can go through this barebones tutorial in which we use the Falcor Router to create a Virtual JSON resource. In this tutorial we will use Falcor's express middleware to serve the Virtual JSON resource on an application server at the URL `/model.json`. We will also host a static web page on the same server which retrieves data from the Virtual JSON resource. ### Creating a Virtual JSON Resource In this example we will use the falcor Router to build a Virtual JSON resource on an app server and host it at `/model.json`. The JSON resource will contain the following contents: ```js { "greeting": "Hello World" } ``` Normally, Routers retrieve the data for their Virtual JSON resource from backend datastores or other web services on-demand. However, in this simple tutorial, the Router will simply return static data for a single key. First we create a folder for our application server. ```bash $ mkdir falcor-app-server $ cd falcor-app-server $ npm init ``` Now we install the falcor Router. ```bash $ npm install falcor-router --save ``` Then install express and falcor-express. Support for restify is also available, as is support for hapi via a [third-party implementation](https://github.com/Netflix/falcor-hapi). ```bash $ npm install express --save $ npm install falcor-express --save ``` Now we create an `index.js` file with the following contents: ```js // index.js const falcorExpress = require("falcor-express"); const Router = require("falcor-router"); const express = require("express"); const app = express(); app.use( "/model.json", falcorExpress.dataSourceRoute(function (req, res) { // create a Virtual JSON resource with single key ('greeting') return new Router([ { // match a request for the key 'greeting' route: "greeting", // respond with a PathValue with the value of 'Hello World.' get: () => ({ path: ["greeting"], value: "Hello World" }), }, ]); }) ); // serve static files from current directory app.use(express.static(__dirname + "/")); app.listen(3000); ``` Now we run the server, which will listen on port `3000` for requests for `/model.json`. ```bash $ node index.js ``` ### Retrieving Data from the Virtual JSON resource Now that we've built a simple virtual JSON document with a single read-only key `greeting`, we will create a test web page and retrieve this key from the server. Create an `index.html` file with the following contents: ```html ``` Now visit `http://localhost:3000/index.html` and you should see the message retrieved from the server: ``` Hello World ``` ## Steps to publish new version - Make pull request with feature/bug fix and tests - Merge pull request into master after code review and passing Travis CI checks - Run `git checkout master` to open `master` branch locally - Run `git pull` to merge latest code, including built `dist/` and `docs/` by Travis - Run `npm run dist` to build `dist/` locally - Ensure the built files are not different from those built by Travis CI, hence creating no change to commit - Update CHANGELOG with features/bug fixes to be released in the new version and commit - Run `npm version patch` (or `minor`, `major`, etc) to create a new git commit and tag - Run `git push origin master && git push --tags` to push code and tags to github - Run `npm publish` to publish the latest version to NPM ## Additional Resources - For detailed high-level documentation explaining the Model, the Router, and JSON Graph check out the [Falcor website](https://netflix.github.io/falcor). - [API documentation](https://netflix.github.io/falcor/doc/Model.html) - For a working example of a Router, check out the [falcor-router-demo](https://github.com/netflix/falcor-router-demo). - For questions and discussion, use [Stack Overflow](https://stackoverflow.com/questions/tagged/falcor). ================================================ FILE: all.js ================================================ var falcor = require("./browser.js"); var Router = require("falcor-router"); falcor.Router = Router; module.exports = falcor; ================================================ FILE: authors.txt ================================================ Jafar Husain Paul Taylor Michael Paulson ================================================ FILE: bower.json ================================================ { "name": "falcor", "main": "./dist/falcor.browser.js", "ignore": [ "build", "examples", "lib", "performance", "test", ".bithoundrc", ".eslintrc", ".gitignore", ".travis.yml", "all.js", "browser.js", "conf.json", "gulpfile.js", "MIGRATIONS.md", "OSSMETADATA", "package.json", "server.js", "webpack.conf.js" ], "dependencies": { } } ================================================ FILE: browser.js ================================================ var falcor = require("./lib"); var jsong = require("falcor-json-graph"); falcor.atom = jsong.atom; falcor.ref = jsong.ref; falcor.error = jsong.error; falcor.pathValue = jsong.pathValue; falcor.HttpDataSource = require("falcor-http-datasource"); module.exports = falcor; ================================================ FILE: build/deploy-ghpages.sh ================================================ #!/bin/bash if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo -e "Building and committing dist and docs...\n" TEMP_DIR=$HOME/tmp FALCOR_DOCS_DIR=$TEMP_DIR/falcordocs FALCOR_BUILD_DIR=$TEMP_DIR/falcorbuild GH_PAGES_DIR=$TEMP_DIR/gh-pages DEPLOYABLE_REPO=git@github.com:Netflix/falcor.git CURRENT_RELEASE=master mkdir -p $TEMP_DIR if [ -d "$FALCOR_BUILD_DIR" ]; then rm -rf $FALCOR_BUILD_DIR fi if [ -d "$FALCOR_DOCS_DIR" ]; then rm -rf $FALCOR_DOCS_DIR fi if [ -d "$GH_PAGES_DIR" ]; then rm -rf $GH_PAGES_DIR fi git config --global user.email "falcorbuild@netflix.com" git config --global user.name "Falcor Build" openssl aes-256-cbc -K $encrypted_00000eb5a141_key -iv $encrypted_00000eb5a141_iv -in deployKey.enc -out deployKey -d chmod 0600 deployKey eval `ssh-agent -s` ssh-add deployKey # Need https url to push changes, and also need to move from detached head to built branch. git remote add deployable $DEPLOYABLE_REPO git checkout $TRAVIS_BRANCH # Generate Docs npm run doc npm run dist git add dist/. git add doc/. git commit -m "Travis build $TRAVIS_BUILD_NUMBER committed dist/ and doc/" git push deployable $TRAVIS_BRANCH if [ "$TRAVIS_BRANCH" == "$CURRENT_RELEASE" ]; then echo -e "Updating gh-pages...\n" cp -R doc $FALCOR_DOCS_DIR cp -R dist $FALCOR_BUILD_DIR # Change Working Directory to $HOME cd $HOME git clone --quiet --branch=gh-pages $DEPLOYABLE_REPO $GH_PAGES_DIR > /dev/null # Change Working Directory to $HOME/gh-pages cd $GH_PAGES_DIR rsync -r $FALCOR_DOCS_DIR/ doc/ rsync -r $FALCOR_BUILD_DIR/ build/ git add . git commit -m "Travis build $TRAVIS_BUILD_NUMBER off $TRAVIS_BRANCH pushed to gh-pages" git push origin gh-pages echo -e "Deployed docs and build to gh-pages\n" fi fi ================================================ FILE: build/falcor-jsdoc-template/README.md ================================================ ### Falcor Website JSDoc Theme Based on the default JSDoc 3 template, with a completely rewritten nav and a number of other improvements. To easily modify it, take a look at the code in navigation.tmpl. Layout.tmpl, container.tmpl. The main container.tmpl file has jekyll front matter in it so the falcor site can use it. If you'd like to use the template both inside and outside the site, it should be as simple as moving the front matter to layout.tmpl, then using the default template's ability to swap out layout.tmpl to have your own standalone template frame. When working with the navigation, you'll notice it generates a long menu with a lot of extra information. That is there so it can be easily styled as you wish with a few lines of CSS. For example, it tracks the active page, so you can adapt which parts of the navigation are visible in a given context. Like the default template for JSDoc 3, it uses: [the Taffy Database library](http://taffydb.com/) and the [Underscore Template library](http://documentcloud.github.com/underscore/#template). ================================================ FILE: build/falcor-jsdoc-template/publish.js ================================================ /*global env: true */ 'use strict'; var doop = require('jsdoc/util/doop'); var fs = require('jsdoc/fs'); var helper = require('jsdoc/util/templateHelper'); var logger = require('jsdoc/util/logger'); var path = require('jsdoc/path'); var taffy = require('taffydb').taffy; var template = require('jsdoc/template'); var util = require('util'); var _ = require('lodash'); var htmlsafe = helper.htmlsafe; var linkto = helper.linkto; var resolveAuthorLinks = helper.resolveAuthorLinks; var hasOwnProp = Object.prototype.hasOwnProperty; var data; var view; var outdir = path.normalize(env.opts.destination); function find(spec) { return helper.find(data, spec); } function tutoriallink(tutorial) { return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' }); } function getAncestorLinks(doclet) { return helper.getAncestorLinks(data, doclet); } function hashToLink(doclet, hash) { if ( !/^(#.+)/.test(hash) ) { return hash; } var url = helper.createLink(doclet); url = url.replace(/(#.+|$)/, hash); return '' + hash + ''; } function needsSignature(doclet) { var needsSig = false; // function and class definitions always get a signature if (doclet.kind === 'function' || doclet.kind === 'class') { needsSig = true; } // typedefs that contain functions get a signature, too else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names && doclet.type.names.length) { for (var i = 0, l = doclet.type.names.length; i < l; i++) { if (doclet.type.names[i].toLowerCase() === 'function') { needsSig = true; break; } } } return needsSig; } function getSignatureAttributes(item) { var attributes = []; if (item.optional) { attributes.push('optional'); } if (item.nullable === true) { attributes.push('nullable'); } else if (item.nullable === false) { attributes.push('non-null'); } return attributes; } function updateItemName(item) { var attributes = getSignatureAttributes(item); var itemName = item.name || ''; if (item.variable) { itemName = '…' + itemName; } if (attributes && attributes.length) { itemName = util.format( '%s%s', itemName, attributes.join(', ') ); } return itemName; } function addParamAttributes(params) { return params.filter(function(param) { return param.name && param.name.indexOf('.') === -1; }).map(updateItemName); } function buildItemTypeStrings(item) { var types = []; if (item && item.type && item.type.names) { item.type.names.forEach(function(name) { types.push( linkto(name, htmlsafe(name)) ); }); } return types; } function buildAttribsString(attribs) { var attribsString = ''; if (attribs && attribs.length) { attribsString = htmlsafe( util.format('(%s) ', attribs.join(', ')) ); } return attribsString; } function addNonParamAttributes(items) { var types = []; items.forEach(function(item) { types = types.concat( buildItemTypeStrings(item) ); }); return types; } function addSignatureParams(f) { var params = f.params ? addParamAttributes(f.params) : []; f.signature = util.format( '%s(%s)', (f.signature || ''), params.join(', ') ); } function addSignatureReturns(f) { var attribs = []; var attribsString = ''; var returnTypes = []; var returnTypesString = ''; // jam all the return-type attributes into an array. this could create odd results (for example, // if there are both nullable and non-nullable return types), but let's assume that most people // who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa. if (f.returns) { f.returns.forEach(function(item) { helper.getAttribs(item).forEach(function(attrib) { if (attribs.indexOf(attrib) === -1) { attribs.push(attrib); } }); }); attribsString = buildAttribsString(attribs); } if (f.returns) { returnTypes = addNonParamAttributes(f.returns); } if (returnTypes.length) { returnTypesString = util.format( ' → %s{%s}', attribsString, returnTypes.join('|') ); } f.signature = '' + (f.signature || '') + '' + '' + returnTypesString + ''; } function addSignatureTypes(f) { var types = f.type ? buildItemTypeStrings(f) : []; f.signature = (f.signature || '') + '' + (types.length ? ' :' + types.join('|') : '') + ''; } function addAttribs(f) { var attribs = helper.getAttribs(f); var attribsString = buildAttribsString(attribs); f.attribs = util.format('%s', attribsString); } function shortenPaths(files, commonPrefix) { Object.keys(files).forEach(function(file) { files[file].shortened = files[file].resolved.replace(commonPrefix, '') // always use forward slashes .replace(/\\/g, '/'); }); return files; } function getPathFromDoclet(doclet) { if (!doclet.meta) { return null; } return doclet.meta.path && doclet.meta.path !== 'null' ? path.join(doclet.meta.path, doclet.meta.filename) : doclet.meta.filename; } function generate(title, docs, filename, resolveLinks) { resolveLinks = resolveLinks === false ? false : true; var docData = { title: title, docs: docs }; var outpath = path.join(outdir, filename), html = view.render('container.tmpl', docData); if (resolveLinks) { html = helper.resolveLinks(html); // turn {@link foo} into foo } fs.writeFileSync(outpath, html, 'utf8'); } function generateSourceFiles(sourceFiles, encoding) { encoding = encoding || 'utf8'; Object.keys(sourceFiles).forEach(function(file) { var source; // links are keyed to the shortened path in each doclet's `meta.shortpath` property var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); helper.registerLink(sourceFiles[file].shortened, sourceOutfile); try { source = { kind: 'source', code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) ) }; } catch(e) { logger.error('Error while generating source file %s: %s', file, e.message); } generate(sourceFiles[file].shortened, [source], sourceOutfile, false); }); } /** * Look for classes or functions with the same name as modules (which indicates that the module * exports only that class or function), then attach the classes or functions to the `module` * property of the appropriate module doclets. The name of each class or function is also updated * for display purposes. This function mutates the original arrays. * * @private * @param {Array.} doclets - The array of classes and functions to * check. * @param {Array.} modules - The array of module doclets to search. */ function attachModuleSymbols(doclets, modules) { var symbols = {}; // build a lookup table doclets.forEach(function(symbol) { symbols[symbol.longname] = symbols[symbol.longname] || []; symbols[symbol.longname].push(symbol); }); return modules.map(function(module) { if (symbols[module.longname]) { module.modules = symbols[module.longname] // Only show symbols that have a description. Make an exception for classes, because // we want to show the constructor-signature heading no matter what. .filter(function(symbol) { return symbol.description || symbol.kind === 'class'; }) .map(function(symbol) { symbol = doop(symbol); if (symbol.kind === 'class' || symbol.kind === 'function') { symbol.name = symbol.name.replace('module:', '(require("') + '"))'; } return symbol; }); } }); } function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) { var nav = ''; if (items.length) { var itemsNav = ''; items.forEach(function(item) { if ( !hasOwnProp.call(item, 'longname') ) { itemsNav += '
  • ' + linktoFn('', item.name) + '
  • '; } else if ( !hasOwnProp.call(itemsSeen, item.longname) ) { itemsNav += '
  • ' + linktoFn(item.longname, item.name.replace(/^module:/, '')) + '
  • '; itemsSeen[item.longname] = true; } }); if (itemsNav !== '') { nav += '

    ' + itemHeading + '

      ' + itemsNav + '
    '; } } return nav; } function linktoTutorial(longName, name) { return tutoriallink(name); } function linktoExternal(longName, name) { return linkto(longName, name.replace(/(^"|"$)/g, '')); } // escapes weird characters that jsdoc puts in ids but jquery chokes on function escapeDocId(docId) { return docId.replace(/(:|\.|\[|\]|,|~)/g, '\\$1'); }; // Returns a link just like linkto, but with an id to its target header on // the page added so bootstrap scrollspy can pick it up use it to work. // Also sanitizes the input because jsdoc uses all sorts of weird characters // in their ids. Optionally, link text can be passed in to use instead of // the doc's name inside the link. function linkToWithTarget(doc, linkText) { var linkTag = linkto(doc.longname, linkText || doc.name); return linkTag.slice(0, linkTag.indexOf('>')) + ' data-target="#' + escapeDocId(doc.id) + '"' + linkTag.slice(linkTag.indexOf('>')); }; /** * Create the navigation sidebar. * @param {object} members The members that will be used to create the sidebar. * @param {array} members.classes * @param {array} members.externals * @param {array} members.globals * @param {array} members.mixins * @param {array} members.modules * @param {array} members.namespaces * @param {array} members.tutorials * @param {array} members.events * @param {array} members.interfaces * @return {string} The HTML for the navigation sidebar. */ function buildNav(members) { var nav = '

    Home

    '; var seen = {}; var seenTutorials = {}; nav += buildMemberNav(members.modules, 'Modules', {}, linkto); nav += buildMemberNav(members.externals, 'Externals', seen, linktoExternal); nav += buildMemberNav(members.classes, 'Classes', seen, linkto); nav += buildMemberNav(members.events, 'Events', seen, linkto); nav += buildMemberNav(members.namespaces, 'Namespaces', seen, linkto); nav += buildMemberNav(members.mixins, 'Mixins', seen, linkto); nav += buildMemberNav(members.tutorials, 'Tutorials', seenTutorials, linktoTutorial); nav += buildMemberNav(members.interfaces, 'Interfaces', seen, linkto); if (members.globals.length) { var globalNav = ''; members.globals.forEach(function(g) { if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { globalNav += '
  • ' + linkto(g.longname, g.name) + '
  • '; } seen[g.longname] = true; }); if (!globalNav) { // turn the heading into a link so you can actually get to the global page nav += '

    ' + linkto('global', 'Global') + '

    '; } else { nav += '

    Global

      ' + globalNav + '
    '; } } return nav; } /** @param {TAFFY} taffyData See . @param {object} opts @param {Tutorial} tutorials */ exports.publish = function(taffyData, opts, tutorials) { data = taffyData; var conf = env.conf.templates || {}; conf.default = conf.default || {}; var templatePath = path.normalize(opts.template); view = new template.Template( path.join(templatePath, 'tmpl') ); // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness // doesn't try to hand them out later var indexUrl = helper.getUniqueFilename('index'); // don't call registerLink() on this one! 'index' is also a valid longname var globalUrl = helper.getUniqueFilename('global'); helper.registerLink('global', globalUrl); // set up templating view.layout = conf.default.layoutFile ? path.getResourcePath(path.dirname(conf.default.layoutFile), path.basename(conf.default.layoutFile) ) : 'layout.tmpl'; // set up tutorials for helper helper.setTutorials(tutorials); data = helper.prune(data); data.sort('longname, version, since'); helper.addEventListeners(data); var sourceFiles = {}; var sourceFilePaths = []; data().each(function(doclet) { doclet.attribs = ''; if (doclet.examples) { doclet.examples = doclet.examples.map(function(example) { var caption, code; if (example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { caption = RegExp.$1; code = RegExp.$3; } return { caption: caption || '', code: code || example }; }); } if (doclet.see) { doclet.see.forEach(function(seeItem, i) { doclet.see[i] = hashToLink(doclet, seeItem); }); } // build a list of source files var sourcePath; if (doclet.meta) { sourcePath = getPathFromDoclet(doclet); sourceFiles[sourcePath] = { resolved: sourcePath, shortened: null }; if (sourceFilePaths.indexOf(sourcePath) === -1) { sourceFilePaths.push(sourcePath); } } }); // update outdir if necessary, then create outdir var packageInfo = ( find({kind: 'package'}) || [] ) [0]; if (packageInfo && packageInfo.name) { outdir = path.join( outdir, packageInfo.name, (packageInfo.version || '') ); } fs.mkPath(outdir); // All static file functionality is handled by the site jekyll templates now // This is left in just in case it needs to be restored later. // copy the template's static files to outdir // var fromDir = path.join(templatePath, 'static'); // var staticFiles = fs.ls(fromDir, 3); // // staticFiles.forEach(function(fileName) { // var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); // fs.mkPath(toDir); // fs.copyFileSync(fileName, toDir); // }); // // // copy user-specified static files to outdir // var staticFilePaths; // var staticFileFilter; // var staticFileScanner; // if (conf.default.staticFiles) { // // The canonical property name is `include`. We accept `paths` for backwards compatibility // // with a bug in JSDoc 3.2.x. // staticFilePaths = conf.default.staticFiles.include || // conf.default.staticFiles.paths || // []; // staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles); // staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); // // staticFilePaths.forEach(function(filePath) { // var extraStaticFiles; // // filePath = path.resolve(env.pwd, filePath); // extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); // // extraStaticFiles.forEach(function(fileName) { // var sourcePath = fs.toDir(filePath); // var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); // fs.mkPath(toDir); // fs.copyFileSync(fileName, toDir); // }); // }); // } if (sourceFilePaths.length) { sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); } data().each(function(doclet) { var url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); // add a shortened version of the full path var docletPath; if (doclet.meta) { docletPath = getPathFromDoclet(doclet); docletPath = sourceFiles[docletPath].shortened; if (docletPath) { doclet.meta.shortpath = docletPath; } } }); data().each(function(doclet) { var url = helper.longnameToUrl[doclet.longname]; if (url.indexOf('#') > -1) { doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); } else { doclet.id = doclet.name; } if ( needsSignature(doclet) ) { addSignatureParams(doclet); addSignatureReturns(doclet); addAttribs(doclet); } }); // do this after the urls have all been generated data().each(function(doclet) { doclet.ancestors = getAncestorLinks(doclet); if (doclet.kind === 'member') { addSignatureTypes(doclet); addAttribs(doclet); } if (doclet.kind === 'constant') { addSignatureTypes(doclet); addAttribs(doclet); doclet.kind = 'member'; } }); var members = helper.getMembers(data); members.tutorials = tutorials.children; // output pretty-printed source files by default var outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false ? true : false; // add template helpers view.find = find; view.linkto = linkto; view.linkToWithTarget = linkToWithTarget; view.resolveAuthorLinks = resolveAuthorLinks; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; view.outputSourceFiles = outputSourceFiles; view._ = _; // once for all view.nav = buildNav(members); attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); // generate the pretty-printed source files first so other pages can link to them if (outputSourceFiles) { generateSourceFiles(sourceFiles, opts.encoding); } if (members.globals.length) { generate('Global', [{kind: 'globalobj'}], globalUrl); } // index page displays information from package.json and lists files var files = find({kind: 'file'}), packages = find({kind: 'package'}); generate('Home', packages.concat( [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] ).concat(files), indexUrl); // set up the lists that we'll use to generate pages var classes = taffy(members.classes); var modules = taffy(members.modules); var namespaces = taffy(members.namespaces); var mixins = taffy(members.mixins); var externals = taffy(members.externals); var interfaces = taffy(members.interfaces); Object.keys(helper.longnameToUrl).forEach(function(longname) { var myModules = helper.find(modules, {longname: longname}); if (myModules.length) { generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); } var myClasses = helper.find(classes, {longname: longname}); if (myClasses.length) { generate('Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); } var myNamespaces = helper.find(namespaces, {longname: longname}); if (myNamespaces.length) { generate('Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); } var myMixins = helper.find(mixins, {longname: longname}); if (myMixins.length) { generate('Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); } var myExternals = helper.find(externals, {longname: longname}); if (myExternals.length) { generate('External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname]); } var myInterfaces = helper.find(interfaces, {longname: longname}); if (myInterfaces.length) { generate('Interface: ' + myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]); } }); // TODO: move the tutorial functions to templateHelper.js function generateTutorial(title, tutorial, filename) { var tutorialData = { title: title, header: tutorial.title, content: tutorial.parse(), children: tutorial.children }; var tutorialPath = path.join(outdir, filename), html = view.render('tutorial.tmpl', tutorialData); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into foo fs.writeFileSync(tutorialPath, html, 'utf8'); } // tutorials can have only one parent so there is no risk for loops function saveChildren(node) { node.children.forEach(function(child) { generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); saveChildren(child); }); } saveChildren(tutorials); }; ================================================ FILE: build/falcor-jsdoc-template/tmpl/augments.tmpl ================================================
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/container.tmpl ================================================ --- layout: api-page title: "" id: api ---

    Global

    Example 1? 's':'' ?>

    Extends

    Requires

    Classes

    Mixins

    Namespaces

    Members

    Methods

    Type Definitions

    Events

    ================================================ FILE: build/falcor-jsdoc-template/tmpl/details.tmpl ================================================ " + data.defaultvalue + ""; defaultObjectClass = ' class="object-value"'; } ?>
    Properties
    Version:
    Since:
    Inherited From:
    Overrides:
    Implementations:
    Implements:
    Mixes In:
    Deprecated:
    Yes
    Author:
    License:
    Default Value:
    >
    Source:
    ,
    Tutorials:
    See:
    To Do:
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/example.tmpl ================================================
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/examples.tmpl ================================================

    ================================================ FILE: build/falcor-jsdoc-template/tmpl/exceptions.tmpl ================================================
    Type
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/layout.tmpl ================================================ ================================================ FILE: build/falcor-jsdoc-template/tmpl/mainpage.tmpl ================================================

    ================================================ FILE: build/falcor-jsdoc-template/tmpl/members.tmpl ================================================

    Type:
    Fires:
    Example 1? 's':'' ?>
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/method.tmpl ================================================

    Constructor

    Extends:
    Type:
    This:
    Parameters:
    Requires:
    Fires:
    Listens to Events:
    Listeners of This Event:
    Throws:
    1) { ?>
    1 || data.returns[0].description) { ?>
    Returns:
    1) { ?>
    Example 1? 's':'' ?>
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/navigation-subgroup.tmpl ================================================
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/navigation.tmpl ================================================ 0) { ?> ================================================ FILE: build/falcor-jsdoc-template/tmpl/params.tmpl ================================================
    Name & Attributes Type Default Description

    optional
    nullable
    repeatable
    Properties
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/properties.tmpl ================================================
    Name Type Attributes Default Description
    <optional>
    <nullable>
    Properties
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/returns.tmpl ================================================
    Return Type:
    ================================================ FILE: build/falcor-jsdoc-template/tmpl/source.tmpl ================================================

    ================================================ FILE: build/falcor-jsdoc-template/tmpl/tutorial.tmpl ================================================
    0) { ?>

    ================================================ FILE: build/falcor-jsdoc-template/tmpl/type.tmpl ================================================ or ================================================ FILE: build/gulp-build.js ================================================ var gulp = require("gulp"); var browserify = require("browserify"); var license = require("gulp-license"); var uglify = require("gulp-uglify"); var vinyl = require("vinyl-source-stream"); var bundleCollapser = require("bundle-collapser/plugin"); var _ = require("lodash"); var path = require("path"); var licenseInfo = { organization: "Netflix, Inc", year: "2020", }; var browserifyOptions = { standalone: "falcor", insertGlobalVars: { Promise: function(file, basedir) { return 'typeof Promise === "function" ? Promise : require("promise")'; }, }, }; function buildDistBrowser() { return build({ file: ["./browser.js"], outName: "falcor.browser", browserifyOptions: browserifyOptions, debug: false, }); } function buildBrowser() { return build({ file: ["./browser.js"], outName: "falcor.browser", browserifyOptions: browserifyOptions, }); } function buildDistAll() { return build({ file: ["./all.js"], outName: "falcor.all", browserifyOptions: browserifyOptions, debug: false, }); } function buildAll() { return build({ file: ["./all.js"], outName: "falcor.all", browserifyOptions: browserifyOptions, }); } function build(options) { options = _.assign( { file: "", browserifyOptions: {}, outName: options.outName, dest: "dist", debug: true, }, options ); var name = options.outName + ((!options.debug && ".min") || "") + ".js"; return browserify(options.file, options.browserifyOptions) .plugin(bundleCollapser) .bundle() .pipe(vinyl(name)) .pipe(license("Apache", licenseInfo)) .pipe(gulp.dest(options.dest)) // eslint-disable-next-line consistent-return .on("finish", function() { if (!options.debug) { // minify output var destAndName = path.join(options.dest, name); return gulp.src(destAndName) .pipe(uglify()) .pipe(gulp.dest(options.dest)); } }); } module.exports = { buildAll: buildAll, buildBrowser: buildBrowser, buildDistAll: buildDistAll, buildDistBrowser: buildDistBrowser, }; ================================================ FILE: build/gulp-clean.js ================================================ var del = require("del"); function cleanPerf() { return del(["./performance/bin", "./performance/out"]); } function cleanDoc() { return del(["./doc"]); } function cleanBin() { return del(["./bin"]); } function cleanDist() { return del(["./dist"]); } function cleanCoverage() { return del(["./coverage"]); } module.exports = { bin: cleanBin, coverage: cleanCoverage, dist: cleanDist, doc: cleanDoc, perf: cleanPerf, }; ================================================ FILE: build/gulp-perf.js ================================================ var gulp = require("gulp"); var concat = require("gulp-concat"); var vinyl = require("vinyl-source-stream"); var browserify = require("browserify"); var gulpShell = require("gulp-shell"); const { runner } = require("karma"); function buildDevice() { return browserify("./performance/device.js", { ignoreMissing: true }) .bundle() .pipe(vinyl("device-body.js")) .pipe(gulp.dest("performance/bin")); } function polyfillDevice() { return gulp .src(["./node_modules/nf-falcor-device-perf/devicePolyfill.js", "performance/bin/device-body.js"], { allowEmpty: true }) .pipe(concat({ path: "device.js" })) .pipe(gulp.dest("performance/bin")); } function buildBrowser() { return browserify("./performance/browser.js") .bundle() .pipe(vinyl("browser.js")) .pipe(gulp.dest("performance/bin")); } function runBrowser() { return gulpShell.task("karma start ./performance/karma.conf.js")(); } function runNode() { return gulpShell.task("node --expose-gc ./performance/node.js")(); } module.exports = { buildDevice: gulp.series(buildDevice, polyfillDevice), buildBrowser: buildBrowser, runBrowser: runBrowser, runNode: runNode, }; ================================================ FILE: build/jsdoc.json ================================================ { "opts": { "template": "falcor-jsdoc-template" }, "templates": {}, "plugins": ["plugins/markdown"] } ================================================ FILE: conf.json ================================================ { "plugins": ["plugins/markdown"] } ================================================ FILE: dist/falcor.all.js ================================================ /*! * Copyright 2020 Netflix, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.falcor = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i} - the requested data as JSON */ Model.prototype.get = require(58); /** * _getOptimizedBoundPath is an extension point for internal users to polyfill * legacy soft-bind behavior, as opposed to deref (hardBind). Current falcor * only supports deref, and assumes _path to be a fully optimized path. * @function * @private * @return {Path} - fully optimized bound path for the model */ Model.prototype._getOptimizedBoundPath = function _getOptimizedBoundPath() { return this._path ? this._path.slice() : this._path; }; /** * The get method retrieves several {@link Path}s or {@link PathSet}s from a {@link Model}. The get method loads each value into a JSON object and returns in a ModelResponse. * @function * @private * @param {Array.} paths - the path(s) to retrieve * @return {ModelResponse.} - the requested data as JSON */ Model.prototype._getWithPaths = require(57); /** * Sets the value at one or more places in the JSONGraph model. The set method accepts one or more {@link PathValue}s, each of which is a combination of a location in the document and the value to place there. In addition to accepting {@link PathValue}s, the set method also returns the values after the set operation is complete. * @function * @return {ModelResponse.} - an {@link Observable} stream containing the values in the JSONGraph model after the set was attempted */ Model.prototype.set = require(61); /** * The preload method retrieves several {@link Path}s or {@link PathSet}s from a {@link Model} and loads them into the Model cache. * @function * @param {...PathSet} path - the path(s) to retrieve * @return {ModelResponse.} - a ModelResponse that completes when the data has been loaded into the cache. */ Model.prototype.preload = function preload() { var out = validateInput(arguments, GET_VALID_INPUT, "preload"); if (out !== true) { return new ModelResponse(function(o) { o.onError(out); }); } var args = Array.prototype.slice.call(arguments); var self = this; return new ModelResponse(function(obs) { return self.get.apply(self, args).subscribe( function() {}, function(err) { obs.onError(err); }, function() { obs.onCompleted(); } ); }); }; /** * Invokes a function in the JSON Graph. * @function * @param {Path} functionPath - the path to the function to invoke * @param {Array.} args - the arguments to pass to the function * @param {Array.} refPaths - the paths to retrieve from the JSON Graph References in the message returned from the function * @param {Array.} extraPaths - additional paths to retrieve after successful function execution * @return {ModelResponse. - a JSONEnvelope contains the values returned from the function */ Model.prototype.call = function call() { var args; var argsIdx = -1; var argsLen = arguments.length; args = new Array(argsLen); while (++argsIdx < argsLen) { var arg = arguments[argsIdx]; args[argsIdx] = arg; var argType = typeof arg; if ( (argsIdx > 1 && !Array.isArray(arg)) || (argsIdx === 0 && !Array.isArray(arg) && argType !== "string") || (argsIdx === 1 && !Array.isArray(arg) && !isPrimitive(arg)) ) { /* eslint-disable no-loop-func */ return new ModelResponse(function(o) { o.onError(new Error("Invalid argument")); }); /* eslint-enable no-loop-func */ } } return new CallResponse(this, args[0], args[1], args[2], args[3]); }; /** * The invalidate method synchronously removes several {@link Path}s or {@link PathSet}s from a {@link Model} cache. * @function * @param {...PathSet} path - the paths to remove from the {@link Model}'s cache. */ Model.prototype.invalidate = function invalidate() { var args; var argsIdx = -1; var argsLen = arguments.length; args = []; while (++argsIdx < argsLen) { args[argsIdx] = pathSyntax.fromPath(arguments[argsIdx]); if (!Array.isArray(args[argsIdx]) || !args[argsIdx].length) { throw new Error("Invalid argument"); } } // creates the obs, subscribes and will throw the errors if encountered. new InvalidateResponse(this, args).subscribe(noOp, function(e) { throw e; }); }; /** * Returns a new {@link Model} bound to a location within the {@link * JSONGraph}. The bound location is never a {@link Reference}: any {@link * Reference}s encountered while resolving the bound {@link Path} are always * replaced with the {@link Reference}s target value. For subsequent operations * on the {@link Model}, all paths will be evaluated relative to the bound * path. Deref allows you to: * - Expose only a fragment of the {@link JSONGraph} to components, rather than * the entire graph * - Hide the location of a {@link JSONGraph} fragment from components * - Optimize for executing multiple operations and path looksup at/below the * same location in the {@link JSONGraph} * @method * @param {Object} responseObject - an object previously retrieved from the * Model * @return {Model} - the dereferenced {@link Model} * @example var Model = falcor.Model; var model = new Model({ cache: { users: [ Model.ref(["usersById", 32]) ], usersById: { 32: { name: "Steve", surname: "McGuire" } } } }); model. get(['users', 0, 'name']). subscribe(function(jsonEnv) { var userModel = model.deref(jsonEnv.json.users[0]); console.log(model.getPath()); console.log(userModel.getPath()); }); }); // prints the following: // [] // ["usersById", 32] - because userModel refers to target of reference at ["users", 0] */ Model.prototype.deref = require(7); /** * A dereferenced model can become invalid when the reference from which it was * built has been removed/collected/expired/etc etc. To fix the issue, a from * the parent request should be made (no parent, then from the root) for a valid * path and re-dereference performed to update what the model is bound too. * * @method * @private * @return {Boolean} - If the currently deref'd model is still considered a * valid deref. */ Model.prototype._hasValidParentReference = require(6); /** * Get data for a single {@link Path}. * @param {Path} path - the path to retrieve * @return {Observable.<*>} - the value for the path * @example var model = new falcor.Model({source: new HttpDataSource("/model.json") }); model. getValue('user.name'). subscribe(function(name) { console.log(name); }); // The code above prints "Jim" to the console. */ Model.prototype.getValue = require(21); /** * Set value for a single {@link Path}. * @param {Path} path - the path to set * @param {Object} value - the value to set * @return {Observable.<*>} - the value for the path * @example var model = new falcor.Model({source: new HttpDataSource("/model.json") }); model. setValue('user.name', 'Jim'). subscribe(function(name) { console.log(name); }); // The code above prints "Jim" to the console. */ Model.prototype.setValue = require(70); // TODO: Does not throw if given a PathSet rather than a Path, not sure if it should or not. // TODO: Doc not accurate? I was able to invoke directly against the Model, perhaps because I don't have a data source? // TODO: Not clear on what it means to "retrieve objects in addition to JSONGraph values" /** * Synchronously retrieves a single path from the local {@link Model} only and will not retrieve missing paths from the {@link DataSource}. This method can only be invoked when the {@link Model} does not have a {@link DataSource} or from within a selector function. See {@link Model.prototype.get}. The getValueSync method differs from the asynchronous get methods (ex. get, getValues) in that it can be used to retrieve objects in addition to JSONGraph values. * @method * @private * @arg {Path} path - the path to retrieve * @return {*} - the value for the specified path */ Model.prototype._getValueSync = require(29); /** * @private */ Model.prototype._setValueSync = require(71); /** * @private */ Model.prototype._derefSync = require(8); /** * Set the local cache to a {@link JSONGraph} fragment. This method can be a useful way of mocking a remote document, or restoring the local cache from a previously stored state. * @param {JSONGraph} jsonGraph - the {@link JSONGraph} fragment to use as the local cache */ Model.prototype.setCache = function modelSetCache(cacheOrJSONGraphEnvelope) { var cache = this._root.cache; if (cacheOrJSONGraphEnvelope !== cache) { var modelRoot = this._root; var boundPath = this._path; this._path = []; this._root.cache = {}; if (typeof cache !== "undefined") { collectLru(modelRoot, modelRoot.expired, getSize(cache), 0); } var out; if (isJSONGraphEnvelope(cacheOrJSONGraphEnvelope)) { out = setJSONGraphs(this, [cacheOrJSONGraphEnvelope])[0]; } else if (isJSONEnvelope(cacheOrJSONGraphEnvelope)) { out = setCache(this, [cacheOrJSONGraphEnvelope])[0]; } else if (isObject(cacheOrJSONGraphEnvelope)) { out = setCache(this, [{ json: cacheOrJSONGraphEnvelope }])[0]; } // performs promotion without producing output. if (out) { get.getWithPathsAsPathMap(this, out, []); } this._path = boundPath; } else if (typeof cache === "undefined") { this._root.cache = {}; } return this; }; /** * Get the local {@link JSONGraph} cache. This method can be a useful to store the state of the cache. * @param {...Array.} [pathSets] - The path(s) to retrieve. If no paths are specified, the entire {@link JSONGraph} is returned. * @return {JSONGraph} all of the {@link JSONGraph} data in the {@link Model} cache. * @example // Storing the boxshot of the first 10 titles in the first 10 genreLists to local storage. localStorage.setItem('cache', JSON.stringify(model.getCache("genreLists[0...10][0...10].boxshot"))); */ Model.prototype.getCache = function _getCache() { var paths = Array.prototype.slice.call(arguments); if (paths.length === 0) { return getCache(this._root.cache); } var result = [{}]; var path = this._path; get.getWithPathsAsJSONGraph(this, paths, result); this._path = path; return result[0].jsonGraph; }; /** * Reset cache maxSize. When the new maxSize is smaller than the old force a collect. * @param {Number} maxSize - the new maximum cache size */ Model.prototype._setMaxSize = function setMaxSize(maxSize) { var oldMaxSize = this._maxSize; this._maxSize = maxSize; if (maxSize < oldMaxSize) { var modelRoot = this._root; var modelCache = modelRoot.cache; // eslint-disable-next-line no-cond-assign var currentVersion = modelCache.$_version; collectLru( modelRoot, modelRoot.expired, getSize(modelCache), this._maxSize, this._collectRatio, currentVersion ); } }; /** * Retrieves a number which is incremented every single time a value is changed underneath the Model or the object at an optionally-provided Path beneath the Model. * @param {Path?} path - a path at which to retrieve the version number * @return {Number} a version number which changes whenever a value is changed underneath the Model or provided Path */ Model.prototype.getVersion = function getVersion(pathArg) { var path = (pathArg && pathSyntax.fromPath(pathArg)) || []; if (Array.isArray(path) === false) { throw new Error("Model#getVersion must be called with an Array path."); } if (this._path.length) { path = this._path.concat(path); } return this._getVersion(this, path); }; Model.prototype._syncCheck = function syncCheck(name) { if (Boolean(this._source) && this._root.syncRefCount <= 0 && this._root.unsafeMode === false) { throw new Error("Model#" + name + " may only be called within the context of a request selector."); } return true; }; /* eslint-disable guard-for-in */ Model.prototype._clone = function cloneModel(opts) { var clone = new this.constructor(this); for (var key in opts) { var value = opts[key]; if (value === "delete") { delete clone[key]; } else { clone[key] = value; } } clone.setCache = void 0; return clone; }; /* eslint-enable */ /** * Returns a clone of the {@link Model} that enables batching. Within the configured time period, * paths for get operations are collected and sent to the {@link DataSource} in a batch. Batching * can be more efficient if the {@link DataSource} access the network, potentially reducing the * number of HTTP requests to the server. * * @param {?Scheduler|number} schedulerOrDelay - Either a {@link Scheduler} that determines when to * send a batch to the {@link DataSource}, or the number in milliseconds to collect a batch before * sending to the {@link DataSource}. If this parameter is omitted, then batch collection ends at * the end of the next tick. * @return {Model} a Model which schedules a batch of get requests to the DataSource. */ Model.prototype.batch = function batch(schedulerOrDelay) { var scheduler; if (typeof schedulerOrDelay === "number") { scheduler = new TimeoutScheduler(Math.round(Math.abs(schedulerOrDelay))); } else if (!schedulerOrDelay || !schedulerOrDelay.schedule) { scheduler = new TimeoutScheduler(1); } else { scheduler = schedulerOrDelay; } var clone = this._clone(); clone._request = new RequestQueue(clone, scheduler); return clone; }; /** * Returns a clone of the {@link Model} that disables batching. This is the default mode. Each get operation will be executed on the {@link DataSource} separately. * @name unbatch * @memberof Model.prototype * @function * @return {Model} a {@link Model} that batches requests of the same type and sends them to the data source together */ Model.prototype.unbatch = function unbatch() { var clone = this._clone(); clone._request = new RequestQueue(clone, new ImmediateScheduler()); return clone; }; /** * Returns a clone of the {@link Model} that treats errors as values. Errors will be reported in the same callback used to report data. Errors will appear as objects in responses, rather than being sent to the {@link Observable~onErrorCallback} callback of the {@link ModelResponse}. * @return {Model} */ Model.prototype.treatErrorsAsValues = function treatErrorsAsValues() { return this._clone({ _treatErrorsAsValues: true }); }; /** * Adapts a Model to the {@link DataSource} interface. * @return {DataSource} * @example var model = new falcor.Model({ cache: { user: { name: "Steve", surname: "McGuire" } } }), proxyModel = new falcor.Model({ source: model.asDataSource() }); // Prints "Steve" proxyModel.getValue("user.name"). then(function(name) { console.log(name); }); */ Model.prototype.asDataSource = function asDataSource() { return new ModelDataSourceAdapter(this); }; Model.prototype._materialize = function materialize() { return this._clone({ _materialized: true }); }; Model.prototype._dematerialize = function dematerialize() { return this._clone({ _materialized: "delete" }); }; /** * Returns a clone of the {@link Model} that boxes values returning the wrapper ({@link Atom}, {@link Reference}, or {@link Error}), rather than the value inside it. This allows any metadata attached to the wrapper to be inspected. * @return {Model} */ Model.prototype.boxValues = function boxValues() { return this._clone({ _boxed: true }); }; /** * Returns a clone of the {@link Model} that unboxes values, returning the value inside of the wrapper ({@link Atom}, {@link Reference}, or {@link Error}), rather than the wrapper itself. This is the default mode. * @return {Model} */ Model.prototype.unboxValues = function unboxValues() { return this._clone({ _boxed: "delete" }); }; /** * Returns a clone of the {@link Model} that only uses the local {@link JSONGraph} and never uses a {@link DataSource} to retrieve missing paths. * @return {Model} */ Model.prototype.withoutDataSource = function withoutDataSource() { return this._clone({ _source: "delete" }); }; Model.prototype.toJSON = function toJSON() { return { $type: "ref", value: this._path }; }; /** * Returns the {@link Path} to the object within the JSON Graph that this Model references. * @return {Path} * @example var Model = falcor.Model; var model = new Model({ cache: { users: [ Model.ref(["usersById", 32]) ], usersById: { 32: { name: "Steve", surname: "McGuire" } } } }); model. get(['users', 0, 'name']). subscribe(function(jsonEnv) { var userModel = model.deref(jsonEnv.json.users[0]); console.log(model.getPath()); console.log(userModel.getPath()); }); }); // prints the following: // [] // ["usersById", 32] - because userModel refers to target of reference at ["users", 0] */ Model.prototype.getPath = function getPath() { return this._path ? this._path.slice() : this._path; }; /** * This one is actually private. I would not use this without talking to * jhusain, sdesai, or michaelbpaulson (github). * @private */ Model.prototype._fromWhenceYouCame = function fromWhenceYouCame(allow) { return this._clone({ _allowFromWhenceYouCame: allow === undefined ? true : allow }); }; Model.prototype._getBoundValue = require(18); Model.prototype._getVersion = require(23); Model.prototype._getPathValuesAsPathMap = get.getWithPathsAsPathMap; Model.prototype._getPathValuesAsJSONG = get.getWithPathsAsJSONGraph; Model.prototype._setPathValues = require(69); Model.prototype._setPathMaps = require(68); Model.prototype._setJSONGs = require(67); Model.prototype._setCache = require(68); Model.prototype._invalidatePathValues = require(39); Model.prototype._invalidatePathMaps = require(38); },{"106":106,"121":121,"125":125,"18":18,"19":19,"21":21,"23":23,"24":24,"29":29,"38":38,"39":39,"4":4,"40":40,"44":44,"5":5,"50":50,"51":51,"52":52,"57":57,"58":58,"59":59,"6":6,"61":61,"65":65,"66":66,"67":67,"68":68,"69":69,"7":7,"70":70,"71":71,"78":78,"8":8,"88":88,"89":89,"90":90,"92":92}],4:[function(require,module,exports){ function ModelDataSourceAdapter(model) { this._model = model._materialize().treatErrorsAsValues(); } ModelDataSourceAdapter.prototype.get = function get(pathSets) { return this._model.get.apply(this._model, pathSets)._toJSONG(); }; ModelDataSourceAdapter.prototype.set = function set(jsongResponse) { return this._model.set(jsongResponse)._toJSONG(); }; ModelDataSourceAdapter.prototype.call = function call(path, args, suffixes, paths) { var params = [path, args, suffixes]; Array.prototype.push.apply(params, paths); return this._model.call.apply(this._model, params)._toJSONG(); }; module.exports = ModelDataSourceAdapter; },{}],5:[function(require,module,exports){ var isFunction = require(86); var hasOwn = require(81); function ModelRoot(o) { var options = o || {}; this.syncRefCount = 0; this.expired = options.expired || []; this.unsafeMode = options.unsafeMode || false; this.cache = {}; if (isFunction(options.comparator)) { this.comparator = options.comparator; } if (isFunction(options.errorSelector)) { this.errorSelector = options.errorSelector; } if (isFunction(options.onChange)) { this.onChange = options.onChange; } } ModelRoot.prototype.errorSelector = function errorSelector(x, y) { return y; }; ModelRoot.prototype.comparator = function comparator(cacheNode, messageNode) { if (hasOwn(cacheNode, "value") && hasOwn(messageNode, "value")) { // They are the same only if the following fields are the same. return cacheNode.value === messageNode.value && cacheNode.$type === messageNode.$type && cacheNode.$expires === messageNode.$expires; } return cacheNode === messageNode; }; module.exports = ModelRoot; },{"81":81,"86":86}],6:[function(require,module,exports){ module.exports = function fromWhenceYeCame() { var reference = this._referenceContainer; // Always true when this mode is false. if (!this._allowFromWhenceYouCame) { return true; } // If fromWhenceYouCame is true and the first set of keys did not have // a reference, this case can happen. They are always valid. if (reference === true) { return true; } // was invalid before even derefing. if (reference === false) { return false; } // Its been disconnected (set over or collected) from the graph. // eslint-disable-next-line camelcase if (reference && reference.$_parent === undefined) { return false; } // The reference has expired but has not been collected from the graph. // eslint-disable-next-line camelcase if (reference && reference.$_invalidated) { return false; } return true; }; },{}],7:[function(require,module,exports){ var InvalidDerefInputError = require(10); var getCachePosition = require(20); var CONTAINER_DOES_NOT_EXIST = "e"; var $ref = require(111); module.exports = function deref(boundJSONArg) { var absolutePath = boundJSONArg && boundJSONArg.$__path; var refPath = boundJSONArg && boundJSONArg.$__refPath; var toReference = boundJSONArg && boundJSONArg.$__toReference; var referenceContainer; // We deref and then ensure that the reference container is attached to // the model. if (absolutePath) { var validContainer = CONTAINER_DOES_NOT_EXIST; if (toReference) { validContainer = false; referenceContainer = getCachePosition(this, toReference); // If the reference container is still a sentinel value then compare // the reference value with refPath. If they are the same, then the // model is still valid. if (refPath && referenceContainer && referenceContainer.$type === $ref) { var containerPath = referenceContainer.value; var i = 0; var len = refPath.length; validContainer = true; for (; validContainer && i < len; ++i) { if (containerPath[i] !== refPath[i]) { validContainer = false; } } } } // Signal to the deref'd model that it has been disconnected from the // graph or there is no _fromWhenceYouCame if (!validContainer) { referenceContainer = false; } // The container did not exist, therefore there is no reference // container and fromWhenceYouCame should always return true. else if (validContainer === CONTAINER_DOES_NOT_EXIST) { referenceContainer = true; } return this._clone({ _path: absolutePath, _referenceContainer: referenceContainer }); } throw new InvalidDerefInputError(); }; },{"10":10,"111":111,"20":20}],8:[function(require,module,exports){ var pathSyntax = require(125); var getBoundValue = require(18); var InvalidModelError = require(11); module.exports = function derefSync(boundPathArg) { var boundPath = pathSyntax.fromPath(boundPathArg); if (!Array.isArray(boundPath)) { throw new Error("Model#derefSync must be called with an Array path."); } var boundValue = getBoundValue(this, this._path.concat(boundPath), false); var path = boundValue.path; var node = boundValue.value; var found = boundValue.found; // If the node is not found or the node is found but undefined is returned, // this happens when a reference is expired. if (!found || node === undefined) { return undefined; } if (node.$type) { throw new InvalidModelError(path, path); } return this._clone({ _path: path }); }; },{"11":11,"125":125,"18":18}],9:[function(require,module,exports){ var applyErrorPrototype = require(15); /** * When a bound model attempts to retrieve JSONGraph it should throw an * error. * * @private */ function BoundJSONGraphModelError() { var instance = new Error("It is not legal to use the JSON Graph " + "format from a bound Model. JSON Graph format" + " can only be used from a root model."); instance.name = "BoundJSONGraphModelError"; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, BoundJSONGraphModelError); } return instance; } applyErrorPrototype(BoundJSONGraphModelError); module.exports = BoundJSONGraphModelError; },{"15":15}],10:[function(require,module,exports){ var applyErrorPrototype = require(15); /** * An invalid deref input is when deref is used with input that is not generated * from a get, set, or a call. * * @private */ function InvalidDerefInputError() { var instance = new Error("Deref can only be used with a non-primitive object from get, set, or call."); instance.name = "InvalidDerefInputError"; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, InvalidDerefInputError); } return instance; } applyErrorPrototype(InvalidDerefInputError); module.exports = InvalidDerefInputError; },{"15":15}],11:[function(require,module,exports){ var applyErrorPrototype = require(15); /** * An InvalidModelError can only happen when a user binds, whether sync * or async to shorted value. See the unit tests for examples. * * @param {*} boundPath * @param {*} shortedPath * * @private */ function InvalidModelError(boundPath, shortedPath) { var instance = new Error("The boundPath of the model is not valid since a value or error was found before the path end."); instance.name = "InvalidModelError"; instance.boundPath = boundPath; instance.shortedPath = shortedPath; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, InvalidModelError); } return instance; } applyErrorPrototype(InvalidModelError); module.exports = InvalidModelError; },{"15":15}],12:[function(require,module,exports){ var applyErrorPrototype = require(15); /** * InvalidSourceError happens when a dataSource syncronously throws * an exception during a get/set/call operation. * * @param {Error} error - The error that was thrown. * * @private */ function InvalidSourceError(error) { var instance = new Error("An exception was thrown when making a request."); instance.name = "InvalidSourceError"; instance.innerError = error; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, InvalidSourceError); } return instance; } applyErrorPrototype(InvalidSourceError); module.exports = InvalidSourceError; },{"15":15}],13:[function(require,module,exports){ var applyErrorPrototype = require(15); /** * A request can only be retried up to a specified limit. Once that * limit is exceeded, then an error will be thrown. * * @param {*} missingOptimizedPaths * * @private */ function MaxRetryExceededError(missingOptimizedPaths) { var instance = new Error("The allowed number of retries have been exceeded."); instance.name = "MaxRetryExceededError"; instance.missingOptimizedPaths = missingOptimizedPaths || []; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, MaxRetryExceededError); } return instance; } applyErrorPrototype(MaxRetryExceededError); MaxRetryExceededError.is = function(e) { return e && e.name === "MaxRetryExceededError"; }; module.exports = MaxRetryExceededError; },{"15":15}],14:[function(require,module,exports){ var applyErrorPrototype = require(15); /** * Does not allow null in path * * @private * @param {Object} [options] - Optional object containing additional error information * @param {Array} [options.requestedPath] - The path that was being processed when the error occurred */ function NullInPathError(options) { var requestedPathString = options && options.requestedPath && options.requestedPath.join ? options.requestedPath.join(", ") : ""; var instance = new Error("`null` and `undefined` are not allowed in branch key positions for requested path: " + requestedPathString); instance.name = "NullInPathError"; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, NullInPathError); } return instance; } applyErrorPrototype(NullInPathError); module.exports = NullInPathError; },{"15":15}],15:[function(require,module,exports){ function applyErrorPrototype(errorType) { errorType.prototype = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf) { Object.setPrototypeOf(errorType, Error); } else { // eslint-disable-next-line errorType.__proto__ = Error; } } module.exports = applyErrorPrototype; },{}],16:[function(require,module,exports){ var createHardlink = require(74); var onValue = require(27); var isExpired = require(31); var $ref = require(111); var promote = require(41); /* eslint-disable no-constant-condition */ function followReference(model, root, nodeArg, referenceContainerArg, referenceArg, seed, isJSONG) { var node = nodeArg; var reference = referenceArg; var referenceContainer = referenceContainerArg; var depth = 0; var k, next; while (true) { if (depth === 0 && referenceContainer.$_context) { depth = reference.length; next = referenceContainer.$_context; } else { k = reference[depth++]; next = node[k]; } if (next) { var type = next.$type; var value = type && next.value || next; if (depth < reference.length) { if (type) { node = next; break; } node = next; continue; } // We need to report a value or follow another reference. else { node = next; if (type && isExpired(next)) { break; } if (!referenceContainer.$_context) { createHardlink(referenceContainer, next); } // Restart the reference follower. if (type === $ref) { // Nulls out the depth, outerResults, if (isJSONG) { onValue(model, next, seed, null, null, null, null, reference, reference.length, isJSONG); } else { promote(model._root, next); } depth = 0; reference = value; referenceContainer = next; node = root; continue; } break; } } else { node = void 0; } break; } if (depth < reference.length && node !== void 0) { var ref = []; for (var i = 0; i < depth; i++) { ref[i] = reference[i]; } reference = ref; } return [node, reference, referenceContainer]; } /* eslint-enable */ module.exports = followReference; },{"111":111,"27":27,"31":31,"41":41,"74":74}],17:[function(require,module,exports){ var getCachePosition = require(20); var InvalidModelError = require(11); var BoundJSONGraphModelError = require(9); function mergeInto(target, obj) { /* eslint guard-for-in: 0 */ if (target === obj) { return; } if (target === null || typeof target !== "object" || target.$type) { return; } if (obj === null || typeof obj !== "object" || obj.$type) { return; } for (var key in obj) { // When merging over a temporary branch structure (for example, as produced by an error selector) // with references, we don't want to mutate the path, particularly because it's also $_absolutePath // on cache nodes if (key === "$__path") { continue; } var targetValue = target[key]; if (targetValue === undefined) { target[key] = obj[key]; } else { mergeInto(targetValue, obj[key]); } } } function defaultEnvelope(isJSONG) { return isJSONG ? {jsonGraph: {}, paths: []} : {json: {}}; } module.exports = function get(walk, isJSONG) { return function innerGet(model, paths, seed) { // Result valueNode not immutable for isJSONG. var nextSeed = isJSONG ? seed : [{}]; var valueNode = nextSeed[0]; var results = { values: nextSeed, optimizedPaths: [] }; var cache = model._root.cache; var boundPath = model._path; var currentCachePosition = cache; var optimizedPath, optimizedLength; var i, len; var requestedPath = []; var derefInfo = []; var referenceContainer; // If the model is bound, then get that cache position. if (boundPath.length) { // JSONGraph output cannot ever be bound or else it will // throw an error. if (isJSONG) { return { criticalError: new BoundJSONGraphModelError() }; } // using _getOptimizedPath because that's a point of extension // for polyfilling legacy falcor optimizedPath = model._getOptimizedBoundPath(); optimizedLength = optimizedPath.length; // We need to get the new cache position path. currentCachePosition = getCachePosition(model, optimizedPath); // If there was a short, then we 'throw an error' to the outside // calling function which will onError the observer. if (currentCachePosition && currentCachePosition.$type) { return { criticalError: new InvalidModelError(boundPath, optimizedPath) }; } referenceContainer = model._referenceContainer; } // Update the optimized path if we else { optimizedPath = []; optimizedLength = 0; } for (i = 0, len = paths.length; i < len; i++) { walk(model, cache, currentCachePosition, paths[i], 0, valueNode, results, derefInfo, requestedPath, optimizedPath, optimizedLength, isJSONG, false, referenceContainer); } // Merge in existing results. // Default to empty envelope if no results were emitted mergeInto(valueNode, paths.length ? seed[0] : defaultEnvelope(isJSONG)); return results; }; }; },{"11":11,"20":20,"9":9}],18:[function(require,module,exports){ var getValueSync = require(22); var InvalidModelError = require(11); module.exports = function getBoundValue(model, pathArg, materialized) { var path = pathArg; var boundPath = pathArg; var boxed, treatErrorsAsValues, value, shorted, found; boxed = model._boxed; materialized = model._materialized; treatErrorsAsValues = model._treatErrorsAsValues; model._boxed = true; model._materialized = materialized === undefined || materialized; model._treatErrorsAsValues = true; value = getValueSync(model, path.concat(null), true); model._boxed = boxed; model._materialized = materialized; model._treatErrorsAsValues = treatErrorsAsValues; path = value.optimizedPath; shorted = value.shorted; found = value.found; value = value.value; while (path.length && path[path.length - 1] === null) { path.pop(); } if (found && shorted) { throw new InvalidModelError(boundPath, path); } return { path: path, value: value, shorted: shorted, found: found }; }; },{"11":11,"22":22}],19:[function(require,module,exports){ var isInternalKey = require(87); /** * decends and copies the cache. */ module.exports = function getCache(cache) { var out = {}; _copyCache(cache, out); return out; }; function cloneBoxedValue(boxedValue) { var clonedValue = {}; var keys = Object.keys(boxedValue); var key; var i; var l; for (i = 0, l = keys.length; i < l; i++) { key = keys[i]; if (!isInternalKey(key)) { clonedValue[key] = boxedValue[key]; } } return clonedValue; } function _copyCache(node, out, fromKey) { // copy and return Object. keys(node). filter(function(k) { // Its not an internal key and the node has a value. In the cache // there are 3 possibilities for values. // 1: A branch node. // 2: A $type-value node. // 3: undefined // We will strip out 3 return !isInternalKey(k) && node[k] !== undefined; }). forEach(function(key) { var cacheNext = node[key]; var outNext = out[key]; if (!outNext) { outNext = out[key] = {}; } // Paste the node into the out cache. if (cacheNext.$type) { var isObject = cacheNext.value && typeof cacheNext.value === "object"; var isUserCreatedcacheNext = !cacheNext.$_modelCreated; var value; if (isObject || isUserCreatedcacheNext) { value = cloneBoxedValue(cacheNext); } else { value = cacheNext.value; } out[key] = value; return; } _copyCache(cacheNext, outNext, key); }); } },{"87":87}],20:[function(require,module,exports){ /** * getCachePosition makes a fast walk to the bound value since all bound * paths are the most possible optimized path. * * @param {Model} model - * @param {Array} path - * @returns {Mixed} - undefined if there is nothing in this position. * @private */ module.exports = function getCachePosition(model, path) { var currentCachePosition = model._root.cache; var depth = -1; var maxDepth = path.length; // The loop is simple now, we follow the current cache position until // while (++depth < maxDepth && currentCachePosition && !currentCachePosition.$type) { currentCachePosition = currentCachePosition[path[depth]]; } return currentCachePosition; }; },{}],21:[function(require,module,exports){ var ModelResponse = require(52); var pathSyntax = require(125); module.exports = function getValue(path) { var parsedPath = pathSyntax.fromPath(path); var pathIdx = 0; var pathLen = parsedPath.length; while (++pathIdx < pathLen) { if (typeof parsedPath[pathIdx] === "object") { /* eslint-disable no-loop-func */ return new ModelResponse(function(o) { o.onError(new Error("Paths must be simple paths")); }); /* eslint-enable no-loop-func */ } } var self = this; return new ModelResponse(function(obs) { return self.get(parsedPath).subscribe(function(data) { var curr = data.json; var depth = -1; var length = parsedPath.length; while (curr && ++depth < length) { curr = curr[parsedPath[depth]]; } obs.onNext(curr); }, function(err) { obs.onError(err); }, function() { obs.onCompleted(); }); }); }; },{"125":125,"52":52}],22:[function(require,module,exports){ var followReference = require(16); var clone = require(30); var isExpired = require(31); var promote = require(41); var $ref = require(111); var $atom = require(109); var $error = require(110); module.exports = function getValueSync(model, simplePath, noClone) { var root = model._root.cache; var len = simplePath.length; var optimizedPath = []; var shorted = false, shouldShort = false; var depth = 0; var key, i, next = root, curr = root, out = root, type, ref, refNode; var found = true; var expired = false; while (next && depth < len) { key = simplePath[depth++]; if (key !== null) { next = curr[key]; optimizedPath[optimizedPath.length] = key; } if (!next) { out = undefined; shorted = true; found = false; break; } type = next.$type; // A materialized item. There is nothing to deref to. if (type === $atom && next.value === undefined) { out = undefined; found = false; shorted = depth < len; break; } // Up to the last key we follow references, ensure that they are not // expired either. if (depth < len) { if (type === $ref) { // If the reference is expired then we need to set expired to // true. if (isExpired(next)) { expired = true; out = undefined; break; } ref = followReference(model, root, root, next, next.value); refNode = ref[0]; // The next node is also set to undefined because nothing // could be found, this reference points to nothing, so // nothing must be returned. if (!refNode) { out = void 0; next = void 0; found = false; break; } type = refNode.$type; next = refNode; optimizedPath = ref[1].slice(0); } if (type) { break; } } // If there is a value, then we have great success, else, report an undefined. else { out = next; } curr = next; } if (depth < len && !expired) { // Unfortunately, if all that follows are nulls, then we have not shorted. for (i = depth; i < len; ++i) { if (simplePath[depth] !== null) { shouldShort = true; break; } } // if we should short or report value. Values are reported on nulls. if (shouldShort) { shorted = true; out = void 0; } else { out = next; } for (i = depth; i < len; ++i) { if (simplePath[i] !== null) { optimizedPath[optimizedPath.length] = simplePath[i]; } } } // promotes if not expired if (out && type) { if (isExpired(out)) { out = void 0; } else { promote(model._root, out); } } // if (out && out.$type === $error && !model._treatErrorsAsValues) { if (out && type === $error && !model._treatErrorsAsValues) { /* eslint-disable no-throw-literal */ throw { path: depth === len ? simplePath : simplePath.slice(0, depth), value: out.value }; /* eslint-enable no-throw-literal */ } else if (out && model._boxed) { out = Boolean(type) && !noClone ? clone(out) : out; } else if (!out && model._materialized) { out = {$type: $atom}; } else if (out) { out = out.value; } return { value: out, shorted: shorted, optimizedPath: optimizedPath, found: found }; }; },{"109":109,"110":110,"111":111,"16":16,"30":30,"31":31,"41":41}],23:[function(require,module,exports){ var getValueSync = require(22); module.exports = function _getVersion(model, path) { // ultra fast clone for boxed values. var gen = getValueSync({ _boxed: true, _root: model._root, _treatErrorsAsValues: model._treatErrorsAsValues }, path, true).value; var version = gen && gen.$_version; return (version == null) ? -1 : version; }; },{"22":22}],24:[function(require,module,exports){ var get = require(17); var walkPath = require(33); var getWithPathsAsPathMap = get(walkPath, false); var getWithPathsAsJSONGraph = get(walkPath, true); module.exports = { getValueSync: require(22), getBoundValue: require(18), getWithPathsAsPathMap: getWithPathsAsPathMap, getWithPathsAsJSONGraph: getWithPathsAsJSONGraph }; },{"17":17,"18":18,"22":22,"33":33}],25:[function(require,module,exports){ var promote = require(41); var clone = require(30); module.exports = function onError(model, node, depth, requestedPath, outerResults) { var value = node.value; if (!outerResults.errors) { outerResults.errors = []; } if (model._boxed) { value = clone(node); } outerResults.errors.push({ path: requestedPath.slice(0, depth), value: value }); promote(model._root, node); }; },{"30":30,"41":41}],26:[function(require,module,exports){ module.exports = function onMissing(model, path, depth, outerResults, requestedPath, optimizedPath, optimizedLength) { var pathSlice; if (!outerResults.requestedMissingPaths) { outerResults.requestedMissingPaths = []; outerResults.optimizedMissingPaths = []; } if (depth < path.length) { // If part of path has not been traversed, we need to ensure that there // are no empty paths (range(1, 0) or empyt array) var isEmpty = false; for (var i = depth; i < path.length && !isEmpty; ++i) { if (isEmptyAtom(path[i])) { return; } } pathSlice = path.slice(depth); } else { pathSlice = []; } concatAndInsertMissing(model, pathSlice, depth, requestedPath, optimizedPath, optimizedLength, outerResults); }; function concatAndInsertMissing(model, remainingPath, depth, requestedPath, optimizedPath, optimizedLength, results) { var requested = requestedPath.slice(0, depth); Array.prototype.push.apply(requested, remainingPath); results.requestedMissingPaths[results.requestedMissingPaths.length] = requested; var optimized = optimizedPath.slice(0, optimizedLength); Array.prototype.push.apply(optimized, remainingPath); results.optimizedMissingPaths[results.optimizedMissingPaths.length] = optimized; } function isEmptyAtom(atom) { if (atom === null || typeof atom !== "object") { return false; } var isArray = Array.isArray(atom); if (isArray && atom.length) { return false; } // Empty array else if (isArray) { return true; } var from = atom.from; var to = atom.to; if (from === undefined || from <= to) { return false; } return true; } },{}],27:[function(require,module,exports){ var promote = require(41); var clone = require(30); var $ref = require(111); var $atom = require(109); var $error = require(110); module.exports = function onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG) { // Promote first. Even if no output is produced we should still promote. if (node) { promote(model._root, node); } // Preload if (!seed) { return; } var i, len, k, key, curr, prev = null, prevK; var materialized = false, valueNode, nodeType = node && node.$type, nodeValue = node && node.value; if (nodeValue === undefined) { materialized = model._materialized; } // materialized if (materialized) { valueNode = {$type: $atom}; } // Boxed Mode will clone the node. else if (model._boxed) { valueNode = clone(node); } // We don't want to emit references in json output else if (!isJSONG && nodeType === $ref) { valueNode = undefined; } // JSONG always clones the node. else if (nodeType === $ref || nodeType === $error) { if (isJSONG) { valueNode = clone(node); } else { valueNode = nodeValue; } } else if (isJSONG) { var isObject = nodeValue && typeof nodeValue === "object"; var isUserCreatedNode = !node || !node.$_modelCreated; if (isObject || isUserCreatedNode) { valueNode = clone(node); } else { valueNode = nodeValue; } } else if (node && nodeType === undefined && nodeValue === undefined) { // Include an empty value for branch nodes valueNode = {}; } else { valueNode = nodeValue; } var hasValues = false; if (isJSONG) { curr = seed.jsonGraph; if (!curr) { hasValues = true; curr = seed.jsonGraph = {}; seed.paths = []; } for (i = 0, len = optimizedLength - 1; i < len; i++) { key = optimizedPath[i]; if (!curr[key]) { hasValues = true; curr[key] = {}; } curr = curr[key]; } // assign the last key = optimizedPath[i]; // TODO: Special case? do string comparisons make big difference? curr[key] = materialized ? {$type: $atom} : valueNode; if (requestedPath) { seed.paths.push(requestedPath.slice(0, depth)); } } // The output is pathMap and the depth is 0. It is just a // value report it as the found JSON else if (depth === 0) { hasValues = true; seed.json = valueNode; } // The output is pathMap but we need to build the pathMap before // reporting the value. else { curr = seed.json; if (!curr) { hasValues = true; curr = seed.json = {}; } for (i = 0; i < depth - 1; i++) { k = requestedPath[i]; // The branch info is already generated output from the walk algo // with the required __path information on it. if (!curr[k]) { hasValues = true; curr[k] = branchInfo[i]; } prev = curr; prevK = k; curr = curr[k]; } k = requestedPath[i]; if (valueNode !== undefined) { if (k != null) { hasValues = true; if (!curr[k]) { curr[k] = valueNode; } } else { // We are protected from reaching here when depth is 1 and prev is // undefined by the InvalidModelError and NullInPathError checks. prev[prevK] = valueNode; } } } if (outerResults) { outerResults.hasValues = hasValues; } }; },{"109":109,"110":110,"111":111,"30":30,"41":41}],28:[function(require,module,exports){ var isExpired = require(31); var $error = require(110); var onError = require(25); var onValue = require(27); var onMissing = require(26); var isMaterialized = require(32); var expireNode = require(76); var currentCacheVersion = require(75); /** * When we land on a valueType (or nothing) then we need to report it out to * the outerResults through errors, missing, or values. * * @private */ module.exports = function onValueType( model, node, path, depth, seed, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG, fromReference) { var currType = node && node.$type; // There are is nothing here, ether report value, or report the value // that is missing. If there is no type then report the missing value. if (!node || !currType) { var materialized = isMaterialized(model); if (materialized || !isJSONG) { onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); } if (!materialized) { onMissing(model, path, depth, outerResults, requestedPath, optimizedPath, optimizedLength); } return; } // If there are expired value, then report it as missing else if (isExpired(node) && !(node.$_version === currentCacheVersion.getVersion() && node.$expires === 0)) { if (!node.$_invalidated) { expireNode(node, model._root.expired, model._root); } onMissing(model, path, depth, outerResults, requestedPath, optimizedPath, optimizedLength); } // If there is an error, then report it as a value if else if (currType === $error) { if (fromReference) { requestedPath[depth] = null; depth += 1; } if (isJSONG || model._treatErrorsAsValues) { onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); } else { onValue(model, undefined, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); onError(model, node, depth, requestedPath, outerResults); } } // Report the value else { if (fromReference) { requestedPath[depth] = null; depth += 1; } onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); } }; },{"110":110,"25":25,"26":26,"27":27,"31":31,"32":32,"75":75,"76":76}],29:[function(require,module,exports){ var pathSyntax = require(125); var getValueSync = require(22); module.exports = function _getValueSync(pathArg) { var path = pathSyntax.fromPath(pathArg); if (Array.isArray(path) === false) { throw new Error("Model#_getValueSync must be called with an Array path."); } if (this._path.length) { path = this._path.concat(path); } this._syncCheck("getValueSync"); return getValueSync(this, path).value; }; },{"125":125,"22":22}],30:[function(require,module,exports){ // Copies the node var privatePrefix = require(35); module.exports = function clone(node) { if (node === undefined) { return node; } var outValue = {}; for (var k in node) { if (k.lastIndexOf(privatePrefix, 0) === 0) { continue; } outValue[k] = node[k]; } return outValue; }; },{"35":35}],31:[function(require,module,exports){ module.exports = require(85); },{"85":85}],32:[function(require,module,exports){ module.exports = function isMaterialized(model) { return model._materialized && !model._source; }; },{}],33:[function(require,module,exports){ var followReference = require(16); var onValueType = require(28); var onValue = require(27); var isExpired = require(31); var iterateKeySet = require(137).iterateKeySet; var $ref = require(111); var promote = require(41); module.exports = function walkPath(model, root, curr, path, depth, seed, outerResults, branchInfo, requestedPath, optimizedPathArg, optimizedLength, isJSONG, fromReferenceArg, referenceContainerArg) { var fromReference = fromReferenceArg; var optimizedPath = optimizedPathArg; var referenceContainer = referenceContainerArg; // The walk is finished when: // - there is no value in the current cache position // - there is a JSONG leaf node in the current cache position // - we've reached the end of the path if (!curr || curr.$type || depth === path.length) { onValueType(model, curr, path, depth, seed, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG, fromReference); return; } var keySet = path[depth]; var isKeySet = keySet !== null && typeof keySet === "object"; var iteratorNote = false; var key = keySet; if (isKeySet) { iteratorNote = {}; key = iterateKeySet(keySet, iteratorNote); } var allowFromWhenceYouCame = model._allowFromWhenceYouCame; var optimizedLengthPlus1 = optimizedLength + 1; var nextDepth = depth + 1; var refPath; // loop over every key in the key set do { if (key == null) { // Skip null/undefined/empty keysets in path and do not descend, // but capture the partial path in the result onValue(model, curr, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); if (iteratorNote && !iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } continue; } fromReference = false; optimizedPath[optimizedLength] = key; requestedPath[depth] = key; var next = curr[key]; var nextOptimizedPath = optimizedPath; var nextOptimizedLength = optimizedLengthPlus1; // If there is the next position we need to consider references. if (next) { var nType = next.$type; var value = nType && next.value || next; // If next is a reference follow it. If we are in JSONG mode, // report that value into the seed without passing the requested // path. If a requested path is passed to onValueType then it // will add that path to the JSONGraph envelope under `paths` if (nextDepth < path.length && nType && nType === $ref && !isExpired(next)) { // promote the node so that the references don't get cleaned up. promote(model._root, next); if (isJSONG) { onValue(model, next, seed, nextDepth, outerResults, null, null, optimizedPath, nextOptimizedLength, isJSONG); } var ref = followReference(model, root, root, next, value, seed, isJSONG); fromReference = true; next = ref[0]; refPath = ref[1]; referenceContainer = ref[2]; nextOptimizedPath = refPath.slice(); nextOptimizedLength = refPath.length; } // The next can be set to undefined by following a reference that // does not exist. if (next) { var obj; // There was a reference container. if (referenceContainer && allowFromWhenceYouCame) { obj = { // eslint-disable-next-line camelcase $__path: next.$_absolutePath, // eslint-disable-next-line camelcase $__refPath: referenceContainer.value, // eslint-disable-next-line camelcase $__toReference: referenceContainer.$_absolutePath }; } // There is no reference container meaning this request was // neither from a model and/or the first n (depth) keys do not // contain references. else { obj = { // eslint-disable-next-line camelcase $__path: next.$_absolutePath }; } branchInfo[depth] = obj; } } // Recurse to the next level. walkPath(model, root, next, path, nextDepth, seed, outerResults, branchInfo, requestedPath, nextOptimizedPath, nextOptimizedLength, isJSONG, fromReference, referenceContainer); // If the iteratorNote is not done, get the next key. if (iteratorNote && !iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (iteratorNote && !iteratorNote.done); }; },{"111":111,"137":137,"16":16,"27":27,"28":28,"31":31,"41":41}],34:[function(require,module,exports){ "use strict"; function falcor(opts) { return new falcor.Model(opts); } /** * A filtering method for keys from a falcor json response. The only gotcha * to this method is when the incoming json is undefined, then undefined will * be returned. * * @public * @param {Object} json - The json response from a falcor model. * @returns {Array} - the keys that are in the model response minus the deref * _private_ meta data. */ falcor.keys = function getJSONKeys(json) { if (!json) { return undefined; } return Object. keys(json). filter(function(key) { return key !== "$__path"; }); }; module.exports = falcor; falcor.Model = require(3); },{"3":3}],35:[function(require,module,exports){ var reservedPrefix = require(37); module.exports = reservedPrefix + "_"; },{"37":37}],36:[function(require,module,exports){ module.exports = require(35) + "ref"; },{"35":35}],37:[function(require,module,exports){ module.exports = "$"; },{}],38:[function(require,module,exports){ var createHardlink = require(74); var __prefix = require(37); var $ref = require(111); var getBoundValue = require(18); var promote = require(41); var getSize = require(78); var hasOwn = require(81); var isObject = require(90); var isExpired = require(85); var isFunction = require(86); var isPrimitive = require(92); var expireNode = require(76); var incrementVersion = require(82); var updateNodeAncestors = require(105); var removeNodeAndDescendants = require(99); /** * Sets a list of PathMaps into a JSON Graph. * @function * @param {Object} model - the Model for which to insert the PathMaps. * @param {Array.} pathMapEnvelopes - the a list of @PathMapEnvelopes to set. */ module.exports = function invalidatePathMaps(model, pathMapEnvelopes) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; var parent = node.$_parent || cache; var initialVersion = cache.$_version; var pathMapIndex = -1; var pathMapCount = pathMapEnvelopes.length; while (++pathMapIndex < pathMapCount) { var pathMapEnvelope = pathMapEnvelopes[pathMapIndex]; invalidatePathMap(pathMapEnvelope.json, cache, parent, node, version, expired, lru); } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } }; function invalidatePathMap(pathMap, root, parent, node, version, expired, lru) { if (isPrimitive(pathMap) || pathMap.$type) { return; } for (var key in pathMap) { if (key[0] !== __prefix && hasOwn(pathMap, key)) { var child = pathMap[key]; var branch = isObject(child) && !child.$type; var results = invalidateNode(root, parent, node, key, branch, expired, lru); var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { invalidatePathMap(child, root, nextParent, nextNode, version, expired, lru); } else if (removeNodeAndDescendants(nextNode, nextParent, key, lru)) { updateNodeAncestors(nextParent, getSize(nextNode), lru, version); } } } } } function invalidateReference(root, node, expired, lru) { if (isExpired(node)) { expireNode(node, expired, lru); return [undefined, root]; } promote(lru, node); var container = node; var reference = node.value; var parent = root; node = node.$_context; if (node != null) { parent = node.$_parent || root; } else { var index = 0; var count = reference.length - 1; parent = node = root; do { var key = reference[index]; var branch = index < count; var results = invalidateNode(root, parent, node, key, branch, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; } while (index++ < count); if (container.$_context !== node) { createHardlink(container, node); } } return [node, parent]; } function invalidateNode(root, parent, node, key, branch, expired, lru) { var type = node.$type; while (type === $ref) { var results = invalidateReference(root, node, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node && node.$type; } if (type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new Error("`null` is not allowed in branch key positions."); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } return [node, parent]; } },{"105":105,"111":111,"18":18,"37":37,"41":41,"74":74,"76":76,"78":78,"81":81,"82":82,"85":85,"86":86,"90":90,"92":92,"99":99}],39:[function(require,module,exports){ var __ref = require(36); var $ref = require(111); var getBoundValue = require(18); var promote = require(41); var getSize = require(78); var isExpired = require(85); var isFunction = require(86); var isPrimitive = require(92); var expireNode = require(76); var iterateKeySet = require(137).iterateKeySet; var incrementVersion = require(82); var updateNodeAncestors = require(105); var removeNodeAndDescendants = require(99); /** * Invalidates a list of Paths in a JSON Graph. * @function * @param {Object} model - the Model for which to insert the PathValues. * @param {Array.} paths - the PathValues to set. */ module.exports = function invalidatePathSets(model, paths) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; // eslint-disable-next-line camelcase var parent = node.$_parent || cache; // eslint-disable-next-line camelcase var initialVersion = cache.$_version; var pathIndex = -1; var pathCount = paths.length; while (++pathIndex < pathCount) { var path = paths[pathIndex]; invalidatePathSet(path, 0, cache, parent, node, version, expired, lru); } // eslint-disable-next-line camelcase var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } }; function invalidatePathSet( path, depth, root, parent, node, version, expired, lru) { var note = {}; var branch = depth < path.length - 1; var keySet = path[depth]; var key = iterateKeySet(keySet, note); do { var results = invalidateNode(root, parent, node, key, branch, expired, lru); var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { invalidatePathSet( path, depth + 1, root, nextParent, nextNode, version, expired, lru ); } else if (removeNodeAndDescendants(nextNode, nextParent, key, lru, undefined)) { updateNodeAncestors(nextParent, getSize(nextNode), lru, version); } } key = iterateKeySet(keySet, note); } while (!note.done); } function invalidateReference(root, node, expired, lru) { if (isExpired(node)) { expireNode(node, expired, lru); return [undefined, root]; } promote(lru, node); var container = node; var reference = node.value; var parent = root; // eslint-disable-next-line camelcase node = node.$_context; if (node != null) { // eslint-disable-next-line camelcase parent = node.$_parent || root; } else { var index = 0; var count = reference.length - 1; parent = node = root; do { var key = reference[index]; var branch = index < count; var results = invalidateNode(root, parent, node, key, branch, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; } while (index++ < count); // eslint-disable-next-line camelcase if (container.$_context !== node) { // eslint-disable-next-line camelcase var backRefs = node.$_refsLength || 0; // eslint-disable-next-line camelcase node.$_refsLength = backRefs + 1; node[__ref + backRefs] = container; // eslint-disable-next-line camelcase container.$_context = node; // eslint-disable-next-line camelcase container.$_refIndex = backRefs; } } return [node, parent]; } function invalidateNode(root, parent, node, key, branch, expired, lru) { var type = node.$type; while (type === $ref) { var results = invalidateReference(root, node, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node.$type; } if (type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new Error("`null` is not allowed in branch key positions."); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } return [node, parent]; } },{"105":105,"111":111,"137":137,"18":18,"36":36,"41":41,"76":76,"78":78,"82":82,"85":85,"86":86,"92":92,"99":99}],40:[function(require,module,exports){ var removeNode = require(98); var updateNodeAncestors = require(105); module.exports = function collect(lru, expired, totalArg, max, ratioArg, version) { var total = totalArg; var ratio = ratioArg; if (typeof ratio !== "number") { ratio = 0.75; } var shouldUpdate = typeof version === "number"; var targetSize = max * ratio; var parent, node, size; node = expired.pop(); while (node) { size = node.$size || 0; total -= size; if (shouldUpdate === true) { updateNodeAncestors(node, size, lru, version); // eslint-disable-next-line camelcase } else if (parent = node.$_parent) { // eslint-disable-line no-cond-assign // eslint-disable-next-line camelcase removeNode(node, parent, node.$_key, lru); } node = expired.pop(); } if (total >= max) { // eslint-disable-next-line camelcase var prev = lru.$_tail; node = prev; while ((total >= targetSize) && node) { // eslint-disable-next-line camelcase prev = prev.$_prev; size = node.$size || 0; total -= size; if (shouldUpdate === true) { updateNodeAncestors(node, size, lru, version); } node = prev; } // eslint-disable-next-line camelcase lru.$_tail = lru.$_prev = node; if (node == null) { // eslint-disable-next-line camelcase lru.$_head = lru.$_next = undefined; } else { // eslint-disable-next-line camelcase node.$_next = undefined; } } }; },{"105":105,"98":98}],41:[function(require,module,exports){ var EXPIRES_NEVER = require(112); // [H] -> Next -> ... -> [T] // [T] -> Prev -> ... -> [H] module.exports = function lruPromote(root, object) { // Never promote node.$expires === 1. They cannot expire. if (object.$expires === EXPIRES_NEVER) { return; } // eslint-disable-next-line camelcase var head = root.$_head; // Nothing is in the cache. if (!head) { // eslint-disable-next-line camelcase root.$_head = root.$_tail = object; return; } if (head === object) { return; } // The item always exist in the cache since to get anything in the // cache it first must go through set. // eslint-disable-next-line camelcase var prev = object.$_prev; // eslint-disable-next-line camelcase var next = object.$_next; if (next) { // eslint-disable-next-line camelcase next.$_prev = prev; } if (prev) { // eslint-disable-next-line camelcase prev.$_next = next; } // eslint-disable-next-line camelcase object.$_prev = undefined; // Insert into head position // eslint-disable-next-line camelcase root.$_head = object; // eslint-disable-next-line camelcase object.$_next = head; // eslint-disable-next-line camelcase head.$_prev = object; // If the item we promoted was the tail, then set prev to tail. // eslint-disable-next-line camelcase if (object === root.$_tail) { // eslint-disable-next-line camelcase root.$_tail = prev; } }; },{"112":112}],42:[function(require,module,exports){ module.exports = function lruSplice(root, object) { // Its in the cache. Splice out. // eslint-disable-next-line camelcase var prev = object.$_prev; // eslint-disable-next-line camelcase var next = object.$_next; if (next) { // eslint-disable-next-line camelcase next.$_prev = prev; } if (prev) { // eslint-disable-next-line camelcase prev.$_next = next; } // eslint-disable-next-line camelcase object.$_prev = object.$_next = undefined; // eslint-disable-next-line camelcase if (object === root.$_head) { // eslint-disable-next-line camelcase root.$_head = next; } // eslint-disable-next-line camelcase if (object === root.$_tail) { // eslint-disable-next-line camelcase root.$_tail = prev; } }; },{}],43:[function(require,module,exports){ var complement = require(46); var flushGetRequest = require(47); var incrementVersion = require(82); var currentCacheVersion = require(75); var REQUEST_ID = 0; var GetRequestType = require(45).GetRequest; var setJSONGraphs = require(67); var setPathValues = require(69); var $error = require(110); var emptyArray = []; var InvalidSourceError = require(12); /** * Creates a new GetRequest. This GetRequest takes a scheduler and * the request queue. Once the scheduler fires, all batched requests * will be sent to the server. Upon request completion, the data is * merged back into the cache and all callbacks are notified. * * @param {Scheduler} scheduler - * @param {RequestQueueV2} requestQueue - * @param {number} attemptCount */ var GetRequestV2 = function(scheduler, requestQueue, attemptCount) { this.sent = false; this.scheduled = false; this.requestQueue = requestQueue; this.id = ++REQUEST_ID; this.type = GetRequestType; this._scheduler = scheduler; this._attemptCount = attemptCount; this._pathMap = {}; this._optimizedPaths = []; this._requestedPaths = []; this._callbacks = []; this._count = 0; this._disposable = null; this._collapsed = null; this._disposed = false; }; GetRequestV2.prototype = { /** * batches the paths that are passed in. Once the request is complete, * all callbacks will be called and the request will be removed from * parent queue. * @param {Array} requestedPaths - * @param {Array} optimizedPaths - * @param {Function} callback - */ batch: function(requestedPaths, optimizedPaths, callback) { var self = this; var batchedOptPathSets = self._optimizedPaths; var batchedReqPathSets = self._requestedPaths; var batchedCallbacks = self._callbacks; var batchIx = batchedOptPathSets.length; // If its not sent, simply add it to the requested paths // and callbacks. batchedOptPathSets[batchIx] = optimizedPaths; batchedReqPathSets[batchIx] = requestedPaths; batchedCallbacks[batchIx] = callback; ++self._count; // If it has not been scheduled, then schedule the action if (!self.scheduled) { self.scheduled = true; var flushedDisposable; var scheduleDisposable = self._scheduler.schedule(function() { flushedDisposable = flushGetRequest(self, batchedOptPathSets, function(err, data) { var i, fn, len; var model = self.requestQueue.model; self.requestQueue.removeRequest(self); self._disposed = true; if (model._treatDataSourceErrorsAsJSONGraphErrors ? err instanceof InvalidSourceError : !!err) { for (i = 0, len = batchedCallbacks.length; i < len; ++i) { fn = batchedCallbacks[i]; if (fn) { fn(err); } } return; } // If there is at least one callback remaining, then // callback the callbacks. if (self._count) { // currentVersion will get added to each inserted // node as node.$_version inside of self._merge. // // atom values just downloaded with $expires: 0 // (now-expired) will get assigned $_version equal // to currentVersion, and checkCacheAndReport will // later consider those nodes to not have expired // for the duration of current event loop tick // // we unset currentCacheVersion after all callbacks // have been called, to ensure that only these // particular callbacks and any synchronous model.get // callbacks inside of these, get the now-expired // values var currentVersion = incrementVersion.getCurrentVersion(); currentCacheVersion.setVersion(currentVersion); var mergeContext = { hasInvalidatedResult: false }; var pathsErr = model._useServerPaths && data && data.paths === undefined ? new Error("Server responses must include a 'paths' field when Model._useServerPaths === true") : undefined; if (!pathsErr) { self._merge(batchedReqPathSets, err, data, mergeContext); } // Call the callbacks. The first one inserts all // the data so that the rest do not have consider // if their data is present or not. for (i = 0, len = batchedCallbacks.length; i < len; ++i) { fn = batchedCallbacks[i]; if (fn) { fn(pathsErr || err, data, mergeContext.hasInvalidatedResult); } } currentCacheVersion.setVersion(null); } }); self._disposable = flushedDisposable; }); // If the scheduler is sync then `flushedDisposable` will be // defined, and we want to use it, because that's what aborts an // in-flight XHR request, for example. // But if the scheduler is async, then `flushedDisposable` won't be // defined yet, and so we must use the scheduler's disposable until // `flushedDisposable` is defined. Since we want to still use // `flushedDisposable` once it is defined (to be able to abort in- // flight XHR requests), hence the reassignment of `_disposable` // above. self._disposable = flushedDisposable || scheduleDisposable; } // Disposes this batched request. This does not mean that the // entire request has been disposed, but just the local one, if all // requests are disposed, then the outer disposable will be removed. return createDisposable(self, batchIx); }, /** * Attempts to add paths to the outgoing request. If there are added * paths then the request callback will be added to the callback list. * Handles adding partial paths as well * * @returns {Array} - whether new requested paths were inserted in this * request, the remaining paths that could not be added, * and disposable for the inserted requested paths. */ add: function(requested, optimized, callback) { // uses the length tree complement calculator. var self = this; var complementResult = complement(requested, optimized, self._pathMap); var inserted = false; var disposable = false; // If we found an intersection, then just add new callback // as one of the dependents of that request if (complementResult.intersection.length) { inserted = true; var batchIx = self._callbacks.length; self._callbacks[batchIx] = callback; self._requestedPaths[batchIx] = complementResult.intersection; self._optimizedPaths[batchIx] = []; ++self._count; disposable = createDisposable(self, batchIx); } return [inserted, complementResult.requestedComplement, complementResult.optimizedComplement, disposable]; }, /** * merges the response into the model"s cache. */ _merge: function(requested, err, data, mergeContext) { var self = this; var model = self.requestQueue.model; var modelRoot = model._root; var errorSelector = modelRoot.errorSelector; var comparator = modelRoot.comparator; var boundPath = model._path; model._path = emptyArray; // flatten all the requested paths, adds them to the var nextPaths = model._useServerPaths ? data.paths : flattenRequestedPaths(requested); // Insert errors in every requested position. if (err && model._treatDataSourceErrorsAsJSONGraphErrors) { var error = err; // Converts errors to objects, a more friendly storage // of errors. if (error instanceof Error) { error = { message: error.message }; } // Not all errors are value $types. if (!error.$type) { error = { $type: $error, value: error }; } var pathValues = nextPaths.map(function(x) { return { path: x, value: error }; }); setPathValues(model, pathValues, null, errorSelector, comparator, mergeContext); } // Insert the jsonGraph from the dataSource. else { setJSONGraphs(model, [{ paths: nextPaths, jsonGraph: data.jsonGraph }], null, errorSelector, comparator, mergeContext); } // return the model"s boundPath model._path = boundPath; } }; // Creates a more efficient closure of the things that are // needed. So the request and the batch index. Also prevents code // duplication. function createDisposable(request, batchIx) { var disposed = false; return function() { if (disposed || request._disposed) { return; } disposed = true; request._callbacks[batchIx] = null; request._optimizedPaths[batchIx] = []; request._requestedPaths[batchIx] = []; // If there are no more requests, then dispose all of the request. var count = --request._count; var disposable = request._disposable; if (count === 0) { // looking for unsubscribe here to support more data sources (Rx) if (disposable.unsubscribe) { disposable.unsubscribe(); } else { disposable.dispose(); } request.requestQueue.removeRequest(request); } }; } function flattenRequestedPaths(requested) { var out = []; var outLen = -1; for (var i = 0, len = requested.length; i < len; ++i) { var paths = requested[i]; for (var j = 0, innerLen = paths.length; j < innerLen; ++j) { out[++outLen] = paths[j]; } } return out; } module.exports = GetRequestV2; },{"110":110,"12":12,"45":45,"46":46,"47":47,"67":67,"69":69,"75":75,"82":82}],44:[function(require,module,exports){ var RequestTypes = require(45); var sendSetRequest = require(48); var GetRequest = require(43); var falcorPathUtils = require(137); /** * The request queue is responsible for queuing the operations to * the model"s dataSource. * * @param {Model} model - * @param {Scheduler} scheduler - */ function RequestQueueV2(model, scheduler) { this.model = model; this.scheduler = scheduler; this.requests = this._requests = []; } RequestQueueV2.prototype = { /** * Sets the scheduler, but will not affect any current requests. */ setScheduler: function(scheduler) { this.scheduler = scheduler; }, /** * performs a set against the dataSource. Sets, though are not batched * currently could be batched potentially in the future. Since no batching * is required the setRequest action is simplified significantly. * * @param {JSONGraphEnvelope} jsonGraph - * @param {number} attemptCount * @param {Function} cb */ set: function(jsonGraph, attemptCount, cb) { if (this.model._enablePathCollapse) { jsonGraph.paths = falcorPathUtils.collapse(jsonGraph.paths); } if (cb === undefined) { cb = attemptCount; attemptCount = undefined; } return sendSetRequest(jsonGraph, this.model, attemptCount, cb); }, /** * Creates a get request to the dataSource. Depending on the current * scheduler is how the getRequest will be flushed. * @param {Array} requestedPaths - * @param {Array} optimizedPaths - * @param {number} attemptCount * @param {Function} cb - */ get: function(requestedPaths, optimizedPaths, attemptCount, cb) { var self = this; var disposables = []; var count = 0; var requests = self._requests; var i, len; var oRemainingPaths = optimizedPaths; var rRemainingPaths = requestedPaths; var disposed = false; var request; if (cb === undefined) { cb = attemptCount; attemptCount = undefined; } for (i = 0, len = requests.length; i < len; ++i) { request = requests[i]; if (request.type !== RequestTypes.GetRequest) { continue; } // The request has been sent, attempt to jump on the request // if possible. if (request.sent) { if (this.model._enableRequestDeduplication) { var results = request.add(rRemainingPaths, oRemainingPaths, refCountCallback); // Checks to see if the results were successfully inserted // into the outgoing results. Then our paths will be reduced // to the complement. if (results[0]) { rRemainingPaths = results[1]; oRemainingPaths = results[2]; disposables[disposables.length] = results[3]; ++count; // If there are no more remaining paths then exit the loop. if (!oRemainingPaths.length) { break; } } } } // If there is an unsent request, then we can batch and leave. else { request.batch(rRemainingPaths, oRemainingPaths, refCountCallback); oRemainingPaths = null; rRemainingPaths = null; ++count; break; } } // After going through all the available requests if there are more // paths to process then a new request must be made. if (oRemainingPaths && oRemainingPaths.length) { request = new GetRequest(self.scheduler, self, attemptCount); requests[requests.length] = request; ++count; var disposable = request.batch(rRemainingPaths, oRemainingPaths, refCountCallback); disposables[disposables.length] = disposable; } // This is a simple refCount callback. function refCountCallback(err, data, hasInvalidatedResult) { if (disposed) { return; } --count; // If the count becomes 0, then its time to notify the // listener that the request is done. if (count === 0) { cb(err, data, hasInvalidatedResult); } } // When disposing the request all of the outbound requests will be // disposed of. return function() { if (disposed || count === 0) { return; } disposed = true; var length = disposables.length; for (var idx = 0; idx < length; ++idx) { disposables[idx](); } }; }, /** * Removes the request from the request queue. */ removeRequest: function(request) { var requests = this._requests; var i = requests.length; while (--i >= 0) { if (requests[i].id === request.id) { requests.splice(i, 1); break; } } } }; module.exports = RequestQueueV2; },{"137":137,"43":43,"45":45,"48":48}],45:[function(require,module,exports){ module.exports = { GetRequest: "GET" }; },{}],46:[function(require,module,exports){ var iterateKeySet = require(137).iterateKeySet; /** * Calculates what paths in requested path sets can be deduplicated based on an existing optimized path tree. * * For path sets with ranges or key sets, if some expanded paths can be found in the path tree, only matching paths are * returned as intersection. The non-matching expanded paths are returned as complement. * * The function returns an object consisting of: * - intersection: requested paths that were matched to the path tree * - optimizedComplement: optimized paths that were not found in the path tree * - requestedComplement: requested paths for the optimized paths that were not found in the path tree */ module.exports = function complement(requested, optimized, tree) { var optimizedComplement = []; var requestedComplement = []; var intersection = []; var i, iLen; for (i = 0, iLen = optimized.length; i < iLen; ++i) { var oPath = optimized[i]; var rPath = requested[i]; var subTree = tree[oPath.length]; var intersectionData = findPartialIntersections(rPath, oPath, subTree); Array.prototype.push.apply(intersection, intersectionData[0]); Array.prototype.push.apply(optimizedComplement, intersectionData[1]); Array.prototype.push.apply(requestedComplement, intersectionData[2]); } return { intersection: intersection, optimizedComplement: optimizedComplement, requestedComplement: requestedComplement }; }; /** * Recursive function to calculate intersection and complement paths in 2 given pathsets at a given depth. * * Parameters: * - requestedPath: full requested path set (can include ranges) * - optimizedPath: corresponding optimized path (can include ranges) * - requestTree: path tree for in-flight request, against which to dedupe * * Returns a 3-tuple consisting of * - the intersection of requested paths with requestTree * - the complement of optimized paths with requestTree * - the complement of corresponding requested paths with requestTree * * Example scenario: * - requestedPath: ['lolomo', 0, 0, 'tags', { from: 0, to: 2 }] * - optimizedPath: ['videosById', 11, 'tags', { from: 0, to: 2 }] * - requestTree: { videosById: 11: { tags: { 0: null, 1: null }}} * * This returns: * [ * [['lolomo', 0, 0, 'tags', 0], ['lolomo', 0, 0, 'tags', 1]], * [['videosById', 11, 'tags', 2]], * [['lolomo', 0, 0, 'tags', 2]] * ] * */ function findPartialIntersections(requestedPath, optimizedPath, requestTree) { var depthDiff = requestedPath.length - optimizedPath.length; var i; // Descend into the request path tree for the optimized-path prefix (when the optimized path is longer than the // requested path) for (i = 0; requestTree && i < -depthDiff; i++) { requestTree = requestTree[optimizedPath[i]]; } // There is no matching path in the request path tree, thus no candidates for deduplication if (!requestTree) { return [[], [optimizedPath], [requestedPath]]; } if (depthDiff === 0) { return recurse(requestedPath, optimizedPath, requestTree, 0, [], []); } else if (depthDiff > 0) { return recurse(requestedPath, optimizedPath, requestTree, 0, requestedPath.slice(0, depthDiff), []); } else { return recurse(requestedPath, optimizedPath, requestTree, -depthDiff, [], optimizedPath.slice(0, -depthDiff)); } } function recurse(requestedPath, optimizedPath, currentTree, depth, rCurrentPath, oCurrentPath) { var depthDiff = requestedPath.length - optimizedPath.length; var intersections = []; var rComplementPaths = []; var oComplementPaths = []; var oPathLen = optimizedPath.length; // Loop over the optimized path, looking for deduplication opportunities for (; depth < oPathLen; ++depth) { var key = optimizedPath[depth]; var keyType = typeof key; if (key && keyType === "object") { // If a range key is found, start an inner loop to iterate over all keys in the range, and add // intersections and complements from each iteration separately. // // Range keys branch out this way, providing individual deduping opportunities for each inner key. var note = {}; var innerKey = iterateKeySet(key, note); while (!note.done) { var nextTree = currentTree[innerKey]; if (nextTree === undefined) { // If no next sub tree exists for an inner key, it's a dead-end and we can add this to // complement paths oComplementPaths[oComplementPaths.length] = arrayConcatSlice2( oCurrentPath, innerKey, optimizedPath, depth + 1 ); rComplementPaths[rComplementPaths.length] = arrayConcatSlice2( rCurrentPath, innerKey, requestedPath, depth + 1 + depthDiff ); } else if (depth === oPathLen - 1) { // Reaching the end of the optimized path means that we found the entire path in the path tree, // so add it to intersections intersections[intersections.length] = arrayConcatElement(rCurrentPath, innerKey); } else { // Otherwise keep trying to find further partial deduping opportunities in the remaining path var intersectionData = recurse( requestedPath, optimizedPath, nextTree, depth + 1, arrayConcatElement(rCurrentPath, innerKey), arrayConcatElement(oCurrentPath, innerKey) ); Array.prototype.push.apply(intersections, intersectionData[0]); Array.prototype.push.apply(oComplementPaths, intersectionData[1]); Array.prototype.push.apply(rComplementPaths, intersectionData[2]); } innerKey = iterateKeySet(key, note); } // The remainder of the path was handled by the recursive call, terminate the loop break; } else { // For simple keys, we don't need to branch out. Loop over `depth` instead of iterating over a range. currentTree = currentTree[key]; oCurrentPath[oCurrentPath.length] = optimizedPath[depth]; rCurrentPath[rCurrentPath.length] = requestedPath[depth + depthDiff]; if (currentTree === undefined) { // The path was not found in the tree, add this to complements oComplementPaths[oComplementPaths.length] = arrayConcatSlice( oCurrentPath, optimizedPath, depth + 1 ); rComplementPaths[rComplementPaths.length] = arrayConcatSlice( rCurrentPath, requestedPath, depth + depthDiff + 1 ); break; } else if (depth === oPathLen - 1) { // The end of optimized path was reached, add to intersections intersections[intersections.length] = rCurrentPath; } } } // Return accumulated intersection and complement paths return [intersections, oComplementPaths, rComplementPaths]; } // Exported for unit testing. module.exports.__test = { findPartialIntersections: findPartialIntersections }; /** * Create a new array consisting of a1 + a subset of a2. Avoids allocating an extra array by calling `slice` on a2. */ function arrayConcatSlice(a1, a2, start) { var result = a1.slice(); var l1 = result.length; var length = a2.length - start; result.length = l1 + length; for (var i = 0; i < length; ++i) { result[l1 + i] = a2[start + i]; } return result; } /** * Create a new array consisting of a1 + a2 + a subset of a3. Avoids allocating an extra array by calling `slice` on a3. */ function arrayConcatSlice2(a1, a2, a3, start) { var result = a1.concat(a2); var l1 = result.length; var length = a3.length - start; result.length = l1 + length; for (var i = 0; i < length; ++i) { result[l1 + i] = a3[start + i]; } return result; } /** * Create a new array consistent of a1 plus an additional element. Avoids the unnecessary array allocation when using `a1.concat([element])`. */ function arrayConcatElement(a1, element) { var result = a1.slice(); result.push(element); return result; } },{"137":137}],47:[function(require,module,exports){ var pathUtils = require(137); var toTree = pathUtils.toTree; var toPaths = pathUtils.toPaths; var InvalidSourceError = require(12); /** * Flushes the current set of requests. This will send the paths to the dataSource. * The results of the dataSource will be sent to callback which should perform the zip of all callbacks. * * @param {GetRequest} request - GetRequestV2 to be flushed to the DataSource * @param {Array} pathSetArrayBatch - Array of Arrays of path sets * @param {Function} callback - * @private */ module.exports = function flushGetRequest(request, pathSetArrayBatch, callback) { if (request._count === 0) { request.requestQueue.removeRequest(request); return null; } request.sent = true; request.scheduled = false; var requestPaths; var model = request.requestQueue.model; if (model._enablePathCollapse || model._enableRequestDeduplication) { // Note on the if-condition: request deduplication uses request._pathMap, // so we need to populate that field if the feature is enabled. // TODO: Move this to the collapse algorithm, // TODO: we should have a collapse that returns the paths and // TODO: the trees. // Take all the paths and add them to the pathMap by length. // Since its a list of paths var pathMap = request._pathMap; var listIdx = 0, listLen = pathSetArrayBatch.length; for (; listIdx < listLen; ++listIdx) { var paths = pathSetArrayBatch[listIdx]; for (var j = 0, pathLen = paths.length; j < pathLen; ++j) { var pathSet = paths[j]; var len = pathSet.length; if (!pathMap[len]) { pathMap[len] = [pathSet]; } else { var pathSetsByLength = pathMap[len]; pathSetsByLength[pathSetsByLength.length] = pathSet; } } } // now that we have them all by length, convert each to a tree. var pathMapKeys = Object.keys(pathMap); var pathMapIdx = 0, pathMapLen = pathMapKeys.length; for (; pathMapIdx < pathMapLen; ++pathMapIdx) { var pathMapKey = pathMapKeys[pathMapIdx]; pathMap[pathMapKey] = toTree(pathMap[pathMapKey]); } } if (model._enablePathCollapse) { // Take the pathMapTree and create the collapsed paths and send those // off to the server. requestPaths = toPaths(request._pathMap); } else if (pathSetArrayBatch.length === 1) { // Single batch Array of path sets, just extract it requestPaths = pathSetArrayBatch[0]; } else { // Multiple batches of Arrays of path sets, shallowly flatten into an Array of path sets requestPaths = Array.prototype.concat.apply([], pathSetArrayBatch); } // Make the request. // You are probably wondering why this is not cancellable. If a request // goes out, and all the requests are removed, the request should not be // cancelled. The reasoning is that another request could come in, after // all callbacks have been removed and be deduped. Might as well keep this // around until it comes back. If at that point there are no requests then // we cancel at the callback above. var getRequest; try { getRequest = model._source.get(requestPaths, request._attemptCount); } catch (e) { callback(new InvalidSourceError()); return null; } // Ensures that the disposable is available for the outside to cancel. var jsonGraphData; var disposable = getRequest.subscribe( function(data) { jsonGraphData = data; }, function(err) { callback(err, jsonGraphData); }, function() { callback(null, jsonGraphData); } ); return disposable; }; },{"12":12,"137":137}],48:[function(require,module,exports){ var setJSONGraphs = require(67); var setPathValues = require(69); var InvalidSourceError = require(12); var emptyArray = []; var emptyDisposable = {dispose: function() {}}; /** * A set request is not an object like GetRequest. It simply only needs to * close over a couple values and its never batched together (at least not now). * * @private * @param {JSONGraphEnvelope} jsonGraph - * @param {Model} model - * @param {number} attemptCount * @param {Function} callback - */ var sendSetRequest = function(originalJsonGraph, model, attemptCount, callback) { var paths = originalJsonGraph.paths; var modelRoot = model._root; var errorSelector = modelRoot.errorSelector; var comparator = modelRoot.comparator; var boundPath = model._path; var resultingJsonGraphEnvelope; // This is analogous to GetRequest _merge / flushGetRequest // SetRequests are just considerably simplier. var setObservable; try { setObservable = model._source. set(originalJsonGraph, attemptCount); } catch (e) { callback(new InvalidSourceError()); return emptyDisposable; } var disposable = setObservable. subscribe(function onNext(jsonGraphEnvelope) { // When disposed, no data is inserted into. This can sync resolve // and if thats the case then its undefined. if (disposable && disposable.disposed) { return; } // onNext will insert all data into the model then save the json // envelope from the incoming result. model._path = emptyArray; var successfulPaths = setJSONGraphs(model, [{ paths: paths, jsonGraph: jsonGraphEnvelope.jsonGraph }], null, errorSelector, comparator); jsonGraphEnvelope.paths = successfulPaths[1]; model._path = boundPath; resultingJsonGraphEnvelope = jsonGraphEnvelope; }, function onError(dataSourceError) { if (disposable && disposable.disposed) { return; } model._path = emptyArray; setPathValues(model, paths.map(function(path) { return { path: path, value: dataSourceError }; }), null, errorSelector, comparator); model._path = boundPath; callback(dataSourceError); }, function onCompleted() { callback(null, resultingJsonGraphEnvelope); }); return disposable; }; module.exports = sendSetRequest; },{"12":12,"67":67,"69":69}],49:[function(require,module,exports){ /** * Will allow for state tracking of the current disposable. Also fulfills the * disposable interface. * @private */ var AssignableDisposable = function AssignableDisposable(disosableCallback) { this.disposed = false; this.currentDisposable = disosableCallback; }; AssignableDisposable.prototype = { /** * Disposes of the current disposable. This would be the getRequestCycle * disposable. */ dispose: function dispose() { if (this.disposed || !this.currentDisposable) { return; } this.disposed = true; // If the current disposable fulfills the disposable interface or just // a disposable function. var currentDisposable = this.currentDisposable; if (currentDisposable.dispose) { currentDisposable.dispose(); } else { currentDisposable(); } } }; module.exports = AssignableDisposable; },{}],50:[function(require,module,exports){ var ModelResponse = require(52); var InvalidSourceError = require(12); var pathSyntax = require(125); /** * @private * @augments ModelResponse */ function CallResponse(model, callPath, args, suffix, paths) { this.callPath = pathSyntax.fromPath(callPath); this.args = args; if (paths) { this.paths = paths.map(pathSyntax.fromPath); } if (suffix) { this.suffix = suffix.map(pathSyntax.fromPath); } this.model = model; } CallResponse.prototype = Object.create(ModelResponse.prototype); CallResponse.prototype._subscribe = function _subscribe(observer) { var callPath = this.callPath; var callArgs = this.args; var suffixes = this.suffix; var extraPaths = this.paths; var model = this.model; var rootModel = model._clone({ _path: [] }); var boundPath = model._path; var boundCallPath = boundPath.concat(callPath); /* eslint-disable consistent-return */ // Precisely the same error as the router when a call function does not // exist. if (!model._source) { observer.onError(new Error("function does not exist")); return; } var response, obs; try { obs = model._source. call(boundCallPath, callArgs, suffixes, extraPaths); } catch (e) { observer.onError(new InvalidSourceError(e)); return; } return obs. subscribe(function(res) { response = res; }, function(err) { observer.onError(err); }, function() { // Run the invalidations first then the follow up JSONGraph set. var invalidations = response.invalidated; if (invalidations && invalidations.length) { rootModel.invalidate.apply(rootModel, invalidations); } // The set rootModel. withoutDataSource(). set(response).subscribe(function(x) { observer.onNext(x); }, function(err) { observer.onError(err); }, function() { observer.onCompleted(); }); }); /* eslint-enable consistent-return */ }; module.exports = CallResponse; },{"12":12,"125":125,"52":52}],51:[function(require,module,exports){ var isArray = Array.isArray; var ModelResponse = require(52); var isPathValue = require(91); var isJSONEnvelope = require(88); var empty = {dispose: function() {}}; function InvalidateResponse(model, args) { // TODO: This should be removed. There should only be 1 type of arguments // coming in, but we have strayed from documentation. this._model = model; var groups = []; var group, groupType; var argIndex = -1; var argCount = args.length; // Validation of arguments have been moved out of this function. while (++argIndex < argCount) { var arg = args[argIndex]; var argType; if (isArray(arg)) { argType = "PathValues"; } else if (isPathValue(arg)) { argType = "PathValues"; } else if (isJSONEnvelope(arg)) { argType = "PathMaps"; } else { throw new Error("Invalid Input"); } if (groupType !== argType) { groupType = argType; group = { inputType: argType, arguments: [] }; groups.push(group); } group.arguments.push(arg); } this._groups = groups; } InvalidateResponse.prototype = Object.create(ModelResponse.prototype); InvalidateResponse.prototype.progressively = function progressively() { return this; }; InvalidateResponse.prototype._toJSONG = function _toJSONG() { return this; }; InvalidateResponse.prototype._subscribe = function _subscribe(observer) { var model = this._model; this._groups.forEach(function(group) { var inputType = group.inputType; var methodArgs = group.arguments; var operationName = "_invalidate" + inputType; var operationFunc = model[operationName]; operationFunc(model, methodArgs); }); observer.onCompleted(); return empty; }; module.exports = InvalidateResponse; },{"52":52,"88":88,"91":91}],52:[function(require,module,exports){ (function (Promise){(function (){ var ModelResponseObserver = require(53); var $$observable = require(310).default; var toEsObservable = require(108); /** * A ModelResponse is a container for the results of a get, set, or call operation performed on a Model. The ModelResponse provides methods which can be used to specify the output format of the data retrieved from a Model, as well as how that data is delivered. * @constructor ModelResponse * @augments Observable */ function ModelResponse(subscribe) { this._subscribe = subscribe; } ModelResponse.prototype[$$observable] = function SymbolObservable() { return toEsObservable(this); }; ModelResponse.prototype._toJSONG = function toJSONG() { return this; }; /** * The progressively method breaks the response up into two parts: the data immediately available in the Model cache, and the data in the Model cache after the missing data has been retrieved from the DataSource. * The progressively method creates a ModelResponse that immediately returns the requested data that is available in the Model cache. If any requested paths are not available in the cache, the ModelResponse will send another JSON message with all of the requested data after it has been retrieved from the DataSource. * @name progressively * @memberof ModelResponse.prototype * @function * @return {ModelResponse.} the values found at the requested paths. * @example var dataSource = (new falcor.Model({ cache: { user: { name: "Steve", surname: "McGuire", age: 31 } } })).asDataSource(); var model = new falcor.Model({ source: dataSource, cache: { user: { name: "Steve", surname: "McGuire" } } }); model. get(["user",["name", "surname", "age"]]). progressively(). // this callback will be invoked twice, once with the data in the // Model cache, and again with the additional data retrieved from the DataSource. subscribe(function(json){ console.log(JSON.stringify(json,null,4)); }); // prints... // { // "json": { // "user": { // "name": "Steve", // "surname": "McGuire" // } // } // } // ...and then prints... // { // "json": { // "user": { // "name": "Steve", // "surname": "McGuire", // "age": 31 // } // } // } */ ModelResponse.prototype.progressively = function progressively() { return this; }; ModelResponse.prototype.subscribe = ModelResponse.prototype.forEach = function subscribe(a, b, c) { var observer = new ModelResponseObserver(a, b, c); var subscription = this._subscribe(observer); switch (typeof subscription) { case "function": return { dispose: function() { if (observer._closed) { return; } observer._closed = true; subscription(); } }; case "object": return { dispose: function() { if (observer._closed) { return; } observer._closed = true; if (subscription !== null) { subscription.dispose(); } } }; default: return { dispose: function() { observer._closed = true; } }; } }; ModelResponse.prototype.then = function then(onNext, onError) { /* global Promise */ var self = this; if (!self._promise) { self._promise = new Promise(function(resolve, reject) { var rejected = false; var values = []; self.subscribe( function(value) { values[values.length] = value; }, function(errors) { rejected = true; reject(errors); }, function() { var value = values; if (values.length <= 1) { value = values[0]; } if (rejected === false) { resolve(value); } } ); }); } return self._promise.then(onNext, onError); }; module.exports = ModelResponse; }).call(this)}).call(this,typeof Promise === "function" ? Promise : require(302)) },{"108":108,"302":302,"310":310,"53":53}],53:[function(require,module,exports){ var noop = require(95); /** * A ModelResponseObserver conform to the Observable's Observer contract. It accepts either an Observer or three optional callbacks which correspond to the Observer methods onNext, onError, and onCompleted. * The ModelResponseObserver wraps an Observer to enforce a variety of different invariants including: * 1. onError callback is only called once. * 2. onCompleted callback is only called once. * @constructor ModelResponseObserver */ function ModelResponseObserver( onNextOrObserver, onErrorFn, onCompletedFn ) { // if callbacks are passed, construct an Observer from them. Create a NOOP function for any missing callbacks. if (!onNextOrObserver || typeof onNextOrObserver !== "object") { this._observer = { onNext: ( typeof onNextOrObserver === "function" ? onNextOrObserver : noop ), onError: ( typeof onErrorFn === "function" ? onErrorFn : noop ), onCompleted: ( typeof onCompletedFn === "function" ? onCompletedFn : noop ) }; } // if an Observer is passed else { this._observer = { onNext: typeof onNextOrObserver.onNext === "function" ? function(value) { onNextOrObserver.onNext(value); } : noop, onError: typeof onNextOrObserver.onError === "function" ? function(error) { onNextOrObserver.onError(error); } : noop, onCompleted: ( typeof onNextOrObserver.onCompleted === "function" ? function() { onNextOrObserver.onCompleted(); } : noop ) }; } } ModelResponseObserver.prototype = { onNext: function(v) { if (!this._closed) { this._observer.onNext(v); } }, onError: function(e) { if (!this._closed) { this._closed = true; this._observer.onError(e); } }, onCompleted: function() { if (!this._closed) { this._closed = true; this._observer.onCompleted(); } } }; module.exports = ModelResponseObserver; },{"95":95}],54:[function(require,module,exports){ var ModelResponse = require(52); var checkCacheAndReport = require(55); var getRequestCycle = require(56); var empty = {dispose: function() {}}; var collectLru = require(40); var getSize = require(78); /** * The get response. It takes in a model and paths and starts * the request cycle. It has been optimized for cache first requests * and closures. * @param {Model} model - * @param {Array} paths - * @augments ModelResponse * @private */ var GetResponse = function GetResponse(model, paths, isJSONGraph, isProgressive, forceCollect) { this.model = model; this.currentRemainingPaths = paths; this.isJSONGraph = isJSONGraph || false; this.isProgressive = isProgressive || false; this.forceCollect = forceCollect || false; }; GetResponse.prototype = Object.create(ModelResponse.prototype); /** * Makes the output of a get response JSONGraph instead of json. * @private */ GetResponse.prototype._toJSONG = function _toJSONGraph() { return new GetResponse(this.model, this.currentRemainingPaths, true, this.isProgressive, this.forceCollect); }; /** * Progressively responding to data in the cache instead of once the whole * operation is complete. * @public */ GetResponse.prototype.progressively = function progressively() { return new GetResponse(this.model, this.currentRemainingPaths, this.isJSONGraph, true, this.forceCollect); }; /** * purely for the purposes of closure creation other than the initial * prototype created closure. * * @private */ GetResponse.prototype._subscribe = function _subscribe(observer) { var seed = [{}]; var errors = []; var model = this.model; var isJSONG = observer.isJSONG = this.isJSONGraph; var isProgressive = this.isProgressive; var results = checkCacheAndReport(model, this.currentRemainingPaths, observer, isProgressive, isJSONG, seed, errors); // If there are no results, finish. if (!results) { if (this.forceCollect) { var modelRoot = model._root; var modelCache = modelRoot.cache; var currentVersion = modelCache.$_version; collectLru(modelRoot, modelRoot.expired, getSize(modelCache), model._maxSize, model._collectRatio, currentVersion); } return empty; } // Starts the async request cycle. return getRequestCycle(this, model, results, observer, errors, 1); }; module.exports = GetResponse; },{"40":40,"52":52,"55":55,"56":56,"78":78}],55:[function(require,module,exports){ var gets = require(24); var getWithPathsAsJSONGraph = gets.getWithPathsAsJSONGraph; var getWithPathsAsPathMap = gets.getWithPathsAsPathMap; /** * Checks cache for the paths and reports if in progressive mode. If * there are missing paths then return the cache hit results. * * Return value (`results`) stores missing path information as 3 index-linked arrays: * `requestedMissingPaths` holds requested paths that were not found in cache * `optimizedMissingPaths` holds optimized versions of requested paths * * Note that requestedMissingPaths is not necessarily the list of paths requested by * user in model.get. It does not contain those paths that were found in * cache. It also breaks some path sets out into separate paths, those which * resolve to different optimized lengths after walking through any references in * cache. * This helps maintain a 1:1 correspondence between requested and optimized missing, * as well as their depth differences (or, length offsets). * * Example: Given cache: `{ lolomo: { 0: $ref('vid'), 1: $ref('a.b.c.d') }}`, * `model.get('lolomo[0..2].name').subscribe()` will result in the following * corresponding values: * index requestedMissingPaths optimizedMissingPaths * 0 ['lolomo', 0, 'name'] ['vid', 'name'] * 1 ['lolomo', 1, 'name'] ['a', 'b', 'c', 'd', 'name'] * 2 ['lolomo', 2, 'name'] ['lolomo', 2, 'name'] * * @param {Model} model - The model that the request was made with. * @param {Array} requestedMissingPaths - * @param {Boolean} progressive - * @param {Boolean} isJSONG - * @param {Function} onNext - * @param {Function} onError - * @param {Function} onCompleted - * @param {Object} seed - The state of the output * @returns {Object} results - * * @private */ module.exports = function checkCacheAndReport(model, requestedPaths, observer, progressive, isJSONG, seed, errors) { // checks the cache for the data. var results = isJSONG ? getWithPathsAsJSONGraph(model, requestedPaths, seed) : getWithPathsAsPathMap(model, requestedPaths, seed); // We are done when there are no missing paths or the model does not // have a dataSource to continue on fetching from. var valueNode = results.values && results.values[0]; var completed = !results.requestedMissingPaths || !results.requestedMissingPaths.length || !model._source; // Copy the errors into the total errors array. if (results.errors) { var errs = results.errors; var errorsLength = errors.length; for (var i = 0, len = errs.length; i < len; ++i, ++errorsLength) { errors[errorsLength] = errs[i]; } } // Report locally available values if: // - the request is in progressive mode, or // - the request is complete and values were found if (progressive || (completed && valueNode !== undefined)) { observer.onNext(valueNode); } // We must communicate critical errors from get that are critical // errors such as bound path is broken or this is a JSONGraph request // with a bound path. if (results.criticalError) { observer.onError(results.criticalError); return null; } // if there are missing paths, then lets return them. if (completed) { if (errors.length) { observer.onError(errors); } else { observer.onCompleted(); } return null; } // Return the results object. return results; }; },{"24":24}],56:[function(require,module,exports){ var checkCacheAndReport = require(55); var MaxRetryExceededError = require(13); var collectLru = require(40); var getSize = require(78); var AssignableDisposable = require(49); var InvalidSourceError = require(12); /** * The get request cycle for checking the cache and reporting * values. If there are missing paths then the async request cycle to * the data source is performed until all paths are resolved or max * requests are made. * @param {GetResponse} getResponse - * @param {Model} model - The model that the request was made with. * @param {Object} results - * @param {Function} onNext - * @param {Function} onError - * @param {Function} onCompleted - * @private */ module.exports = function getRequestCycle(getResponse, model, results, observer, errors, count) { // we have exceeded the maximum retry limit. if (count > model._maxRetries) { observer.onError(new MaxRetryExceededError(results.optimizedMissingPaths)); return { dispose: function() {} }; } var requestQueue = model._request; var requestedMissingPaths = results.requestedMissingPaths; var optimizedMissingPaths = results.optimizedMissingPaths; var disposable = new AssignableDisposable(); // We need to prepend the bound path to all requested missing paths and // pass those into the requestQueue. var boundRequestedMissingPaths = []; var boundPath = model._path; if (boundPath.length) { for (var i = 0, len = requestedMissingPaths.length; i < len; ++i) { boundRequestedMissingPaths[i] = boundPath.concat(requestedMissingPaths[i]); } } // No bound path, no array copy and concat. else { boundRequestedMissingPaths = requestedMissingPaths; } var currentRequestDisposable = requestQueue. get(boundRequestedMissingPaths, optimizedMissingPaths, count, function(err, data, hasInvalidatedResult) { if (model._treatDataSourceErrorsAsJSONGraphErrors ? err instanceof InvalidSourceError : !!err) { if (results.hasValues) { observer.onNext(results.values && results.values[0]); } observer.onError(err); return; } var nextRequestedMissingPaths; var nextSeed; // If merging over an existing branch structure with refs has invalidated our intermediate json, // we want to start over and re-get all requested paths with a fresh seed if (hasInvalidatedResult) { nextRequestedMissingPaths = getResponse.currentRemainingPaths; nextSeed = [{}]; } else { nextRequestedMissingPaths = requestedMissingPaths; nextSeed = results.values; } // Once the request queue finishes, check the cache and bail if // we can. var nextResults = checkCacheAndReport(model, nextRequestedMissingPaths, observer, getResponse.isProgressive, getResponse.isJSONGraph, nextSeed, errors); // If there are missing paths coming back form checkCacheAndReport // the its reported from the core cache check method. if (nextResults) { // update the which disposable to use. disposable.currentDisposable = getRequestCycle(getResponse, model, nextResults, observer, errors, count + 1); } // We have finished. Since we went to the dataSource, we must // collect on the cache. else { var modelRoot = model._root; var modelCache = modelRoot.cache; var currentVersion = modelCache.$_version; collectLru(modelRoot, modelRoot.expired, getSize(modelCache), model._maxSize, model._collectRatio, currentVersion); } }); disposable.currentDisposable = currentRequestDisposable; return disposable; }; },{"12":12,"13":13,"40":40,"49":49,"55":55,"78":78}],57:[function(require,module,exports){ var GetResponse = require(54); /** * Performs a get on the cache and if there are missing paths * then the request will be forwarded to the get request cycle. * @private */ module.exports = function getWithPaths(paths) { return new GetResponse(this, paths); }; },{"54":54}],58:[function(require,module,exports){ var pathSyntax = require(125); var ModelResponse = require(52); var GET_VALID_INPUT = require(59); var validateInput = require(106); var GetResponse = require(54); /** * Performs a get on the cache and if there are missing paths * then the request will be forwarded to the get request cycle. * @private */ module.exports = function get() { // Validates the input. If the input is not pathSets or strings then we // will onError. var out = validateInput(arguments, GET_VALID_INPUT, "get"); if (out !== true) { return new ModelResponse(function(o) { o.onError(out); }); } var paths = pathSyntax.fromPathsOrPathValues(arguments); return new GetResponse(this, paths); }; },{"106":106,"125":125,"52":52,"54":54,"59":59}],59:[function(require,module,exports){ module.exports = { path: true, pathSyntax: true }; },{}],60:[function(require,module,exports){ var ModelResponse = require(52); var pathSyntax = require(125); var isArray = Array.isArray; var isPathValue = require(91); var isJSONGraphEnvelope = require(89); var isJSONEnvelope = require(88); var setRequestCycle = require(63); /** * The set response is responsible for doing the request loop for the set * operation and subscribing to the follow up get. * * The constructors job is to parse out the arguments and put them in their * groups. The following subscribe will do the actual cache set and dataSource * operation remoting. * * @param {Model} model - * @param {Array} args - The array of arguments that can be JSONGraph, JSON, or * pathValues. * @param {Boolean} isJSONGraph - if the request is a jsonGraph output format. * @param {Boolean} isProgressive - progressive output. * @augments ModelResponse * @private */ var SetResponse = function SetResponse(model, args, isJSONGraph, isProgressive) { // The response properties. this._model = model; this._isJSONGraph = isJSONGraph || false; this._isProgressive = isProgressive || false; this._initialArgs = args; this._value = [{}]; var groups = []; var group, groupType; var argIndex = -1; var argCount = args.length; // Validation of arguments have been moved out of this function. while (++argIndex < argCount) { var arg = args[argIndex]; var argType; if (isArray(arg) || typeof arg === "string") { arg = pathSyntax.fromPath(arg); argType = "PathValues"; } else if (isPathValue(arg)) { arg.path = pathSyntax.fromPath(arg.path); argType = "PathValues"; } else if (isJSONGraphEnvelope(arg)) { argType = "JSONGs"; } else if (isJSONEnvelope(arg)) { argType = "PathMaps"; } if (groupType !== argType) { groupType = argType; group = { inputType: argType, arguments: [] }; groups.push(group); } group.arguments.push(arg); } this._groups = groups; }; SetResponse.prototype = Object.create(ModelResponse.prototype); /** * The subscribe function will setup the remoting of the operation and cache * setting. * * @private */ SetResponse.prototype._subscribe = function _subscribe(observer) { var groups = this._groups; var model = this._model; var isJSONGraph = this._isJSONGraph; var isProgressive = this._isProgressive; // Starts the async request cycle. return setRequestCycle( model, observer, groups, isJSONGraph, isProgressive, 1); }; /** * Makes the output of a get response JSONGraph instead of json. * @private */ SetResponse.prototype._toJSONG = function _toJSONGraph() { return new SetResponse(this._model, this._initialArgs, true, this._isProgressive); }; /** * Progressively responding to data in the cache instead of once the whole * operation is complete. * @public */ SetResponse.prototype.progressively = function progressively() { return new SetResponse(this._model, this._initialArgs, this._isJSONGraph, true); }; module.exports = SetResponse; },{"125":125,"52":52,"63":63,"88":88,"89":89,"91":91}],61:[function(require,module,exports){ var setValidInput = require(64); var validateInput = require(106); var SetResponse = require(60); var ModelResponse = require(52); module.exports = function set() { var out = validateInput(arguments, setValidInput, "set"); if (out !== true) { return new ModelResponse(function(o) { o.onError(out); }); } var argsIdx = -1; var argsLen = arguments.length; var args = []; while (++argsIdx < argsLen) { args[argsIdx] = arguments[argsIdx]; } return new SetResponse(this, args); }; },{"106":106,"52":52,"60":60,"64":64}],62:[function(require,module,exports){ var arrayFlatMap = require(72); /** * Takes the groups that are created in the SetResponse constructor and sets * them into the cache. */ module.exports = function setGroupsIntoCache(model, groups) { var modelRoot = model._root; var errorSelector = modelRoot.errorSelector; var groupIndex = -1; var groupCount = groups.length; var requestedPaths = []; var optimizedPaths = []; var returnValue = { requestedPaths: requestedPaths, optimizedPaths: optimizedPaths }; // Takes each of the groups and normalizes their input into // requested paths and optimized paths. while (++groupIndex < groupCount) { var group = groups[groupIndex]; var inputType = group.inputType; var methodArgs = group.arguments; if (methodArgs.length > 0) { var operationName = "_set" + inputType; var operationFunc = model[operationName]; var successfulPaths = operationFunc(model, methodArgs, null, errorSelector); optimizedPaths.push.apply(optimizedPaths, successfulPaths[1]); if (inputType === "PathValues") { requestedPaths.push.apply(requestedPaths, methodArgs.map(pluckPath)); } else if (inputType === "JSONGs") { requestedPaths.push.apply(requestedPaths, arrayFlatMap(methodArgs, pluckEnvelopePaths)); } else { requestedPaths.push.apply(requestedPaths, successfulPaths[0]); } } } return returnValue; }; function pluckPath(pathValue) { return pathValue.path; } function pluckEnvelopePaths(jsonGraphEnvelope) { return jsonGraphEnvelope.paths; } },{"72":72}],63:[function(require,module,exports){ var emptyArray = []; var AssignableDisposable = require(49); var GetResponse = require(54); var setGroupsIntoCache = require(62); var getWithPathsAsPathMap = require(24).getWithPathsAsPathMap; var InvalidSourceError = require(12); var MaxRetryExceededError = require(13); /** * The request cycle for set. This is responsible for requesting to dataSource * and allowing disposing inflight requests. */ module.exports = function setRequestCycle(model, observer, groups, isJSONGraph, isProgressive, count) { var requestedAndOptimizedPaths = setGroupsIntoCache(model, groups); var optimizedPaths = requestedAndOptimizedPaths.optimizedPaths; var requestedPaths = requestedAndOptimizedPaths.requestedPaths; // we have exceeded the maximum retry limit. if (count > model._maxRetries) { observer.onError(new MaxRetryExceededError(optimizedPaths)); return { dispose: function() {} }; } var isMaster = model._source === undefined; // Local set only. We perform a follow up get. If performance is ever // a requirement simply requiring in checkCacheAndReport and use get request // internals. Figured this is more "pure". if (isMaster) { return subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph, isProgressive); } // Progressively output the data from the first set. var prevVersion; if (isProgressive) { var results = getWithPathsAsPathMap(model, requestedPaths, [{}]); if (results.criticalError) { observer.onError(results.criticalError); return null; } observer.onNext(results.values[0]); prevVersion = model._root.cache.$_version; } var currentJSONGraph = getJSONGraph(model, optimizedPaths); var disposable = new AssignableDisposable(); // Sends out the setRequest. The Queue will call the callback with the // JSONGraph envelope / error. var requestDisposable = model._request. // TODO: There is error handling that has not been addressed yet. // If disposed before this point then the sendSetRequest will not // further any callbacks. Therefore, if we are at this spot, we are // not disposed yet. set(currentJSONGraph, count, function(error, jsonGraphEnv) { if (error instanceof InvalidSourceError) { observer.onError(error); return; } // TODO: This seems like there are errors with this approach, but // for sanity sake I am going to keep this logic in here until a // rethink can be done. var isCompleted = false; if (error || optimizedPaths.length === jsonGraphEnv.paths.length) { isCompleted = true; } // If we're in progressive mode and nothing changed in the meantime, we're done if (isProgressive) { var nextVersion = model._root.cache.$_version; var versionChanged = nextVersion !== prevVersion; if (!versionChanged) { observer.onCompleted(); return; } } // Happy case. One request to the dataSource will fulfill the // required paths. if (isCompleted) { disposable.currentDisposable = subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph, isProgressive); } // TODO: The unhappy case. I am unsure how this can even be // achieved. else { // We need to restart the setRequestCycle. setRequestCycle(model, observer, groups, isJSONGraph, isProgressive, count + 1); } }); // Sets the current disposable as the requestDisposable. disposable.currentDisposable = requestDisposable; return disposable; }; function getJSONGraph(model, optimizedPaths) { var boundPath = model._path; var envelope = {}; model._path = emptyArray; model._getPathValuesAsJSONG(model._materialize().withoutDataSource(), optimizedPaths, [envelope]); model._path = boundPath; return envelope; } function subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph, isProgressive) { // Creates a new response and subscribes to it with the original observer. // Also sets forceCollect to true, incase the operation is synchronous and // exceeds the cache limit size var response = new GetResponse(model, requestedPaths, isJSONGraph, isProgressive, true); return response.subscribe(observer); } },{"12":12,"13":13,"24":24,"49":49,"54":54,"62":62}],64:[function(require,module,exports){ module.exports = { pathValue: true, pathSyntax: true, json: true, jsonGraph: true }; },{}],65:[function(require,module,exports){ var empty = {dispose: function() {}}; function ImmediateScheduler() {} ImmediateScheduler.prototype.schedule = function schedule(action) { action(); return empty; }; ImmediateScheduler.prototype.scheduleWithState = function scheduleWithState(state, action) { action(this, state); return empty; }; module.exports = ImmediateScheduler; },{}],66:[function(require,module,exports){ function TimeoutScheduler(delay) { this.delay = delay; } var TimerDisposable = function TimerDisposable(id) { this.id = id; this.disposed = false; }; TimeoutScheduler.prototype.schedule = function schedule(action) { var id = setTimeout(action, this.delay); return new TimerDisposable(id); }; TimeoutScheduler.prototype.scheduleWithState = function scheduleWithState(state, action) { var self = this; var id = setTimeout(function() { action(self, state); }, this.delay); return new TimerDisposable(id); }; TimerDisposable.prototype.dispose = function() { if (this.disposed) { return; } clearTimeout(this.id); this.disposed = true; }; module.exports = TimeoutScheduler; },{}],67:[function(require,module,exports){ var createHardlink = require(74); var $ref = require(111); var isExpired = require(84); var isFunction = require(86); var isPrimitive = require(92); var expireNode = require(76); var iterateKeySet = require(137).iterateKeySet; var incrementVersion = require(82); var mergeJSONGraphNode = require(93); var NullInPathError = require(14); /** * Merges a list of {@link JSONGraphEnvelope}s into a {@link JSONGraph}. * @function * @param {Object} model - the Model for which to merge the {@link JSONGraphEnvelope}s. * @param {Array.} jsonGraphEnvelopes - the {@link JSONGraphEnvelope}s to merge. * @return {Array.>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values. */ module.exports = function setJSONGraphs(model, jsonGraphEnvelopes, x, errorSelector, comparator, replacedPaths) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var cache = modelRoot.cache; var initialVersion = cache.$_version; var requestedPath = []; var optimizedPath = []; var requestedPaths = []; var optimizedPaths = []; var jsonGraphEnvelopeIndex = -1; var jsonGraphEnvelopeCount = jsonGraphEnvelopes.length; while (++jsonGraphEnvelopeIndex < jsonGraphEnvelopeCount) { var jsonGraphEnvelope = jsonGraphEnvelopes[jsonGraphEnvelopeIndex]; var paths = jsonGraphEnvelope.paths; var jsonGraph = jsonGraphEnvelope.jsonGraph; var pathIndex = -1; var pathCount = paths.length; while (++pathIndex < pathCount) { var path = paths[pathIndex]; optimizedPath.index = 0; setJSONGraphPathSet( path, 0, cache, cache, cache, jsonGraph, jsonGraph, jsonGraph, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); } } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } return [requestedPaths, optimizedPaths]; }; /* eslint-disable no-constant-condition */ function setJSONGraphPathSet( path, depth, root, parent, node, messageRoot, messageParent, message, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var note = {}; var branch = depth < path.length - 1; var keySet = path[depth]; var key = iterateKeySet(keySet, note); var optimizedIndex = optimizedPath.index; do { requestedPath.depth = depth; var results = setNode( root, parent, node, messageRoot, messageParent, message, key, branch, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); requestedPath[depth] = key; requestedPath.index = depth; optimizedPath[optimizedPath.index++] = key; var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { setJSONGraphPathSet( path, depth + 1, root, nextParent, nextNode, messageRoot, results[3], results[2], requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); } else { requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1)); optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index)); } } key = iterateKeySet(keySet, note); if (note.done) { break; } optimizedPath.index = optimizedIndex; } while (true); } /* eslint-enable */ var _result = new Array(4); function setReference( root, node, messageRoot, message, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var reference = node.value; optimizedPath.length = 0; optimizedPath.push.apply(optimizedPath, reference); if (isExpired(node)) { optimizedPath.index = reference.length; expireNode(node, expired, lru); _result[0] = undefined; _result[1] = root; _result[2] = message; _result[3] = messageRoot; return _result; } var index = 0; var container = node; var count = reference.length - 1; var parent = node = root; var messageParent = message = messageRoot; do { var key = reference[index]; var branch = index < count; optimizedPath.index = index; var results = setNode( root, parent, node, messageRoot, messageParent, message, key, branch, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { optimizedPath.index = index; return results; } parent = results[1]; message = results[2]; messageParent = results[3]; } while (index++ < count); optimizedPath.index = index; if (container.$_context !== node) { createHardlink(container, node); } _result[0] = node; _result[1] = parent; _result[2] = message; _result[3] = messageParent; return _result; } function setNode( root, parent, node, messageRoot, messageParent, message, key, branch, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var type = node.$type; while (type === $ref) { var results = setReference( root, node, messageRoot, message, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; message = results[2]; messageParent = results[3]; type = node.$type; } if (type !== void 0) { _result[0] = node; _result[1] = parent; _result[2] = message; _result[3] = messageParent; return _result; } if (key == null) { if (branch) { throw new NullInPathError({ requestedPath: requestedPath }); } else if (node) { key = node.$_key; } } else { parent = node; messageParent = message; node = parent[key]; message = messageParent && messageParent[key]; } node = mergeJSONGraphNode( parent, node, message, key, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); _result[0] = node; _result[1] = parent; _result[2] = message; _result[3] = messageParent; return _result; } },{"111":111,"137":137,"14":14,"74":74,"76":76,"82":82,"84":84,"86":86,"92":92,"93":93}],68:[function(require,module,exports){ var createHardlink = require(74); var __prefix = require(37); var $ref = require(111); var getBoundValue = require(18); var isArray = Array.isArray; var hasOwn = require(81); var isObject = require(90); var isExpired = require(85); var isFunction = require(86); var isPrimitive = require(92); var expireNode = require(76); var incrementVersion = require(82); var mergeValueOrInsertBranch = require(94); var NullInPathError = require(14); /** * Sets a list of {@link PathMapEnvelope}s into a {@link JSONGraph}. * @function * @param {Object} model - the Model for which to insert the PathMaps. * @param {Array.} pathMapEnvelopes - the a list of {@link PathMapEnvelope}s to set. * @return {Array.>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values. */ module.exports = function setPathMaps(model, pathMapEnvelopes, x, errorSelector, comparator) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; var parent = node.$_parent || cache; var initialVersion = cache.$_version; var requestedPath = []; var requestedPaths = []; var optimizedPaths = []; var optimizedIndex = bound.length; var pathMapIndex = -1; var pathMapCount = pathMapEnvelopes.length; while (++pathMapIndex < pathMapCount) { var pathMapEnvelope = pathMapEnvelopes[pathMapIndex]; var optimizedPath = bound.slice(0); optimizedPath.index = optimizedIndex; setPathMap( pathMapEnvelope.json, 0, cache, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } return [requestedPaths, optimizedPaths]; }; /* eslint-disable no-constant-condition */ function setPathMap( pathMap, depth, root, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector) { var keys = getKeys(pathMap); if (keys && keys.length) { var keyIndex = 0; var keyCount = keys.length; var optimizedIndex = optimizedPath.index; do { var key = keys[keyIndex]; var child = pathMap[key]; var branch = isObject(child) && !child.$type; requestedPath.depth = depth; var results = setNode( root, parent, node, key, child, branch, false, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); requestedPath[depth] = key; requestedPath.index = depth; optimizedPath[optimizedPath.index++] = key; var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { setPathMap( child, depth + 1, root, nextParent, nextNode, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } else { requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1)); optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index)); } } if (++keyIndex >= keyCount) { break; } optimizedPath.index = optimizedIndex; } while (true); } } /* eslint-enable */ function setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector) { var reference = node.value; optimizedPath.length = 0; optimizedPath.push.apply(optimizedPath, reference); if (isExpired(node)) { optimizedPath.index = reference.length; expireNode(node, expired, lru); return [undefined, root]; } var container = node; var parent = root; node = node.$_context; if (node != null) { parent = node.$_parent || root; optimizedPath.index = reference.length; } else { var index = 0; var count = reference.length - 1; optimizedPath.index = index; parent = node = root; do { var key = reference[index]; var branch = index < count; var results = setNode( root, parent, node, key, value, branch, true, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); node = results[0]; if (isPrimitive(node)) { optimizedPath.index = index; return results; } parent = results[1]; } while (index++ < count); optimizedPath.index = index; if (container.$_context !== node) { createHardlink(container, node); } } return [node, parent]; } function setNode( root, parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector) { var type = node.$type; while (type === $ref) { var results = setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node && node.$type; } if (type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new NullInPathError({ requestedPath: requestedPath }); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } node = mergeValueOrInsertBranch( parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); return [node, parent]; } function getKeys(pathMap) { if (isObject(pathMap) && !pathMap.$type) { var keys = []; var itr = 0; if (isArray(pathMap)) { keys[itr++] = "length"; } for (var key in pathMap) { if (key[0] === __prefix || !hasOwn(pathMap, key)) { continue; } keys[itr++] = key; } return keys; } return void 0; } },{"111":111,"14":14,"18":18,"37":37,"74":74,"76":76,"81":81,"82":82,"85":85,"86":86,"90":90,"92":92,"94":94}],69:[function(require,module,exports){ var createHardlink = require(74); var $ref = require(111); var getBoundValue = require(18); var isExpired = require(85); var isFunction = require(86); var isPrimitive = require(92); var expireNode = require(76); var iterateKeySet = require(137).iterateKeySet; var incrementVersion = require(82); var mergeValueOrInsertBranch = require(94); var NullInPathError = require(14); /** * Sets a list of {@link PathValue}s into a {@link JSONGraph}. * @function * @param {Object} model - the Model for which to insert the {@link PathValue}s. * @param {Array.} pathValues - the list of {@link PathValue}s to set. * @return {Array.>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values. */ module.exports = function setPathValues(model, pathValues, x, errorSelector, comparator) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; var parent = node.$_parent || cache; var initialVersion = cache.$_version; var requestedPath = []; var requestedPaths = []; var optimizedPaths = []; var optimizedIndex = bound.length; var pathValueIndex = -1; var pathValueCount = pathValues.length; while (++pathValueIndex < pathValueCount) { var pathValue = pathValues[pathValueIndex]; var path = pathValue.path; var value = pathValue.value; var optimizedPath = bound.slice(0); optimizedPath.index = optimizedIndex; setPathSet( value, path, 0, cache, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } return [requestedPaths, optimizedPaths]; }; /* eslint-disable no-constant-condition */ function setPathSet( value, path, depth, root, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var note = {}; var branch = depth < path.length - 1; var keySet = path[depth]; var key = iterateKeySet(keySet, note); var optimizedIndex = optimizedPath.index; do { requestedPath.depth = depth; var results = setNode( root, parent, node, key, value, branch, false, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); requestedPath[depth] = key; requestedPath.index = depth; optimizedPath[optimizedPath.index++] = key; var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { setPathSet( value, path, depth + 1, root, nextParent, nextNode, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } else { requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1)); optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index)); } } key = iterateKeySet(keySet, note); if (note.done) { break; } optimizedPath.index = optimizedIndex; } while (true); } /* eslint-enable */ function setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var reference = node.value; optimizedPath.length = 0; optimizedPath.push.apply(optimizedPath, reference); if (isExpired(node)) { optimizedPath.index = reference.length; expireNode(node, expired, lru); return [undefined, root]; } var container = node; var parent = root; node = node.$_context; if (node != null) { parent = node.$_parent || root; optimizedPath.index = reference.length; } else { var index = 0; var count = reference.length - 1; parent = node = root; do { var key = reference[index]; var branch = index < count; optimizedPath.index = index; var results = setNode( root, parent, node, key, value, branch, true, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { optimizedPath.index = index; return results; } parent = results[1]; } while (index++ < count); optimizedPath.index = index; if (container.$_context !== node) { createHardlink(container, node); } } return [node, parent]; } function setNode( root, parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var type = node.$type; while (type === $ref) { var results = setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node.$type; } if (branch && type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new NullInPathError({ requestedPath: requestedPath }); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } node = mergeValueOrInsertBranch( parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); return [node, parent]; } },{"111":111,"137":137,"14":14,"18":18,"74":74,"76":76,"82":82,"85":85,"86":86,"92":92,"94":94}],70:[function(require,module,exports){ var jsong = require(121); var ModelResponse = require(52); var isPathValue = require(91); module.exports = function setValue(pathArg, valueArg) { var value = isPathValue(pathArg) ? pathArg : jsong.pathValue(pathArg, valueArg); var pathIdx = 0; var path = value.path; var pathLen = path.length; while (++pathIdx < pathLen) { if (typeof path[pathIdx] === "object") { /* eslint-disable no-loop-func */ return new ModelResponse(function(o) { o.onError(new Error("Paths must be simple paths")); }); /* eslint-enable no-loop-func */ } } var self = this; return new ModelResponse(function(obs) { return self.set(value).subscribe(function(data) { var curr = data.json; var depth = -1; var length = path.length; while (curr && ++depth < length) { curr = curr[path[depth]]; } obs.onNext(curr); }, function(err) { obs.onError(err); }, function() { obs.onCompleted(); }); }); }; },{"121":121,"52":52,"91":91}],71:[function(require,module,exports){ var pathSyntax = require(125); var isPathValue = require(91); var setPathValues = require(69); module.exports = function setValueSync(pathArg, valueArg, errorSelectorArg, comparatorArg) { var path = pathSyntax.fromPath(pathArg); var value = valueArg; var errorSelector = errorSelectorArg; // XXX comparator is never used. var comparator = comparatorArg; if (isPathValue(path)) { comparator = errorSelector; errorSelector = value; value = path; } else { value = { path: path, value: value }; } if (isPathValue(value) === false) { throw new Error("Model#setValueSync must be called with an Array path."); } if (typeof errorSelector !== "function") { errorSelector = this._root._errorSelector; } if (typeof comparator !== "function") { comparator = this._root._comparator; } this._syncCheck("setValueSync"); setPathValues(this, [value]); return this._getValueSync(value.path); }; },{"125":125,"69":69,"91":91}],72:[function(require,module,exports){ module.exports = function arrayFlatMap(array, selector) { var index = -1; var i = -1; var n = array.length; var array2 = []; while (++i < n) { var array3 = selector(array[i], i, array); var j = -1; var k = array3.length; while (++j < k) { array2[++index] = array3[j]; } } return array2; }; },{}],73:[function(require,module,exports){ var privatePrefix = require(35); var hasOwn = require(81); var isArray = Array.isArray; var isObject = require(90); module.exports = function clone(value) { var dest = value; if (isObject(dest)) { dest = isArray(value) ? [] : {}; var src = value; for (var key in src) { if (key.lastIndexOf(privatePrefix, 0) === 0 || !hasOwn(src, key)) { continue; } dest[key] = src[key]; } } return dest; }; },{"35":35,"81":81,"90":90}],74:[function(require,module,exports){ var __ref = require(36); module.exports = function createHardlink(from, to) { // create a back reference // eslint-disable-next-line camelcase var backRefs = to.$_refsLength || 0; to[__ref + backRefs] = from; // eslint-disable-next-line camelcase to.$_refsLength = backRefs + 1; // create a hard reference // eslint-disable-next-line camelcase from.$_refIndex = backRefs; // eslint-disable-next-line camelcase from.$_context = to; }; },{"36":36}],75:[function(require,module,exports){ var version = null; exports.setVersion = function setCacheVersion(newVersion) { version = newVersion; }; exports.getVersion = function getCacheVersion() { return version; }; },{}],76:[function(require,module,exports){ var splice = require(42); module.exports = function expireNode(node, expired, lru) { // eslint-disable-next-line camelcase if (!node.$_invalidated) { // eslint-disable-next-line camelcase node.$_invalidated = true; expired.push(node); splice(lru, node); } return node; }; },{"42":42}],77:[function(require,module,exports){ var isObject = require(90); module.exports = function getSize(node) { return isObject(node) && node.$expires || undefined; }; },{"90":90}],78:[function(require,module,exports){ var isObject = require(90); module.exports = function getSize(node) { return isObject(node) && node.$size || 0; }; },{"90":90}],79:[function(require,module,exports){ var isObject = require(90); module.exports = function getTimestamp(node) { return isObject(node) && node.$timestamp || undefined; }; },{"90":90}],80:[function(require,module,exports){ var isObject = require(90); module.exports = function getType(node, anyType) { var type = isObject(node) && node.$type || void 0; if (anyType && type) { return "branch"; } return type; }; },{"90":90}],81:[function(require,module,exports){ var isObject = require(90); var hasOwn = Object.prototype.hasOwnProperty; module.exports = function(obj, prop) { return isObject(obj) && hasOwn.call(obj, prop); }; },{"90":90}],82:[function(require,module,exports){ var version = 1; module.exports = function incrementVersion() { return version++; }; module.exports.getCurrentVersion = function getCurrentVersion() { return version; }; },{}],83:[function(require,module,exports){ /* eslint-disable camelcase */ module.exports = function insertNode(node, parent, key, version, optimizedPath) { node.$_key = key; node.$_parent = parent; if (version !== undefined) { node.$_version = version; } if (!node.$_absolutePath) { if (Array.isArray(key)) { node.$_absolutePath = optimizedPath.slice(0, optimizedPath.index); Array.prototype.push.apply(node.$_absolutePath, key); } else { node.$_absolutePath = optimizedPath.slice(0, optimizedPath.index); node.$_absolutePath.push(key); } } parent[key] = node; return node; }; },{}],84:[function(require,module,exports){ var now = require(96); var $now = require(113); var $never = require(112); module.exports = function isAlreadyExpired(node) { var exp = node.$expires; return (exp != null) && ( exp !== $never) && ( exp !== $now) && ( exp < now()); }; },{"112":112,"113":113,"96":96}],85:[function(require,module,exports){ var now = require(96); var $now = require(113); var $never = require(112); module.exports = function isExpired(node) { var exp = node.$expires; return (exp != null) && ( exp !== $never ) && ( exp === $now || exp < now()); }; },{"112":112,"113":113,"96":96}],86:[function(require,module,exports){ var functionTypeof = "function"; module.exports = function isFunction(func) { return Boolean(func) && typeof func === functionTypeof; }; },{}],87:[function(require,module,exports){ var privatePrefix = require(35); /** * Determined if the key passed in is an internal key. * * @param {String} x The key * @private * @returns {Boolean} */ module.exports = function isInternalKey(x) { return x === "$size" || x.lastIndexOf(privatePrefix, 0) === 0; }; },{"35":35}],88:[function(require,module,exports){ var isObject = require(90); module.exports = function isJSONEnvelope(envelope) { return isObject(envelope) && ("json" in envelope); }; },{"90":90}],89:[function(require,module,exports){ var isArray = Array.isArray; var isObject = require(90); module.exports = function isJSONGraphEnvelope(envelope) { return isObject(envelope) && isArray(envelope.paths) && ( isObject(envelope.jsonGraph) || isObject(envelope.jsong) || isObject(envelope.json) || isObject(envelope.values) || isObject(envelope.value) ); }; },{"90":90}],90:[function(require,module,exports){ var objTypeof = "object"; module.exports = function isObject(value) { return value !== null && typeof value === objTypeof; }; },{}],91:[function(require,module,exports){ var isArray = Array.isArray; var isObject = require(90); module.exports = function isPathValue(pathValue) { return isObject(pathValue) && ( isArray(pathValue.path) || ( typeof pathValue.path === "string" )); }; },{"90":90}],92:[function(require,module,exports){ var objTypeof = "object"; module.exports = function isPrimitive(value) { return value == null || typeof value !== objTypeof; }; },{}],93:[function(require,module,exports){ var $ref = require(111); var $error = require(110); var getSize = require(78); var getTimestamp = require(79); var isObject = require(90); var isExpired = require(85); var isFunction = require(86); var wrapNode = require(107); var insertNode = require(83); var expireNode = require(76); var replaceNode = require(100); var updateNodeAncestors = require(105); var reconstructPath = require(97); module.exports = function mergeJSONGraphNode( parent, node, message, key, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var sizeOffset; var cType, mType, cIsObject, mIsObject, cTimestamp, mTimestamp; var nodeValue = node && node.value !== undefined ? node.value : node; // If the cache and message are the same, we can probably return early: // - If they're both nullsy, // - If null then the node needs to be wrapped in an atom and inserted. // This happens from whole branch merging when a leaf is just a null value // instead of being wrapped in an atom. // - If undefined then return null (previous behavior). // - If they're both branches, return the branch. // - If they're both edges, continue below. if (nodeValue === message) { // There should not be undefined values. Those should always be // wrapped in an $atom if (message === null) { node = wrapNode(message, undefined, message); parent = updateNodeAncestors(parent, -node.$size, lru, version); node = insertNode(node, parent, key, undefined, optimizedPath); return node; } // The messange and cache are both undefined, therefore return null. else if (message === undefined) { return message; } else { cIsObject = isObject(node); if (cIsObject) { // Is the cache node a branch? If so, return the cache branch. cType = node.$type; if (cType == null) { // Has the branch been introduced to the cache yet? If not, // give it a parent, key, and absolute path. if (node.$_parent == null) { insertNode(node, parent, key, version, optimizedPath); } return node; } } } } else { cIsObject = isObject(node); if (cIsObject) { cType = node.$type; } } // If the cache isn't a reference, we might be able to return early. if (cType !== $ref) { mIsObject = isObject(message); if (mIsObject) { mType = message.$type; } if (cIsObject && !cType) { // If the cache is a branch and the message is empty or // also a branch, continue with the cache branch. if (message == null || (mIsObject && !mType)) { return node; } } } // If the cache is a reference, we might not need to replace it. else { // If the cache is a reference, but the message is empty, leave the cache alone... if (message == null) { // ...unless the cache is an expired reference. In that case, expire // the cache node and return undefined. if (isExpired(node)) { expireNode(node, expired, lru); return void 0; } return node; } mIsObject = isObject(message); if (mIsObject) { mType = message.$type; // If the cache and the message are both references, // check if we need to replace the cache reference. if (mType === $ref) { if (node === message) { // If the cache and message are the same reference, // we performed a whole-branch merge of one of the // grandparents. If we've previously graphed this // reference, break early. Otherwise, continue to // leaf insertion below. if (node.$_parent != null) { return node; } } else { cTimestamp = node.$timestamp; mTimestamp = message.$timestamp; // - If either the cache or message reference is expired, // replace the cache reference with the message. // - If neither of the references are expired, compare their // timestamps. If either of them don't have a timestamp, // or the message's timestamp is newer, replace the cache // reference with the message reference. // - If the message reference is older than the cache // reference, short-circuit. if (!isExpired(node) && !isExpired(message) && mTimestamp < cTimestamp) { return void 0; } } } } } // If the cache is a leaf but the message is a branch, merge the branch over the leaf. if (cType && mIsObject && !mType) { return insertNode(replaceNode(node, message, parent, key, lru, replacedPaths), parent, key, undefined, optimizedPath); } // If the message is a sentinel or primitive, insert it into the cache. else if (mType || !mIsObject) { // If the cache and the message are the same value, we branch-merged one // of the message's ancestors. If this is the first time we've seen this // leaf, give the message a $size and $type, attach its graph pointers, // and update the cache sizes and versions. if (mType === $error && isFunction(errorSelector)) { message = errorSelector(reconstructPath(requestedPath, key), message); mType = message.$type || mType; } if (mType && node === message) { if (node.$_parent == null) { node = wrapNode(node, mType, node.value); parent = updateNodeAncestors(parent, -node.$size, lru, version); node = insertNode(node, parent, key, version, optimizedPath); } } // If the cache and message are different, the cache value is expired, // or the message is a primitive, replace the cache with the message value. // If the message is a sentinel, clone and maintain its type. // If the message is a primitive value, wrap it in an atom. else { var isDistinct = true; // If the cache is a branch, but the message is a leaf, replace the // cache branch with the message leaf. if ((cType && !isExpired(node)) || !cIsObject) { // Compare the current cache value with the new value. If either of // them don't have a timestamp, or the message's timestamp is newer, // replace the cache value with the message value. If a comparator // is specified, the comparator takes precedence over timestamps. // // Comparing either Number or undefined to undefined always results in false. isDistinct = (getTimestamp(message) < getTimestamp(node)) === false; // If at least one of the cache/message are sentinels, compare them. if (isDistinct && (cType || mType) && isFunction(comparator)) { isDistinct = !comparator(nodeValue, message, optimizedPath.slice(0, optimizedPath.index)); } } if (isDistinct) { message = wrapNode(message, mType, mType ? message.value : message); sizeOffset = getSize(node) - getSize(message); node = replaceNode(node, message, parent, key, lru, replacedPaths); parent = updateNodeAncestors(parent, sizeOffset, lru, version); node = insertNode(node, parent, key, version, optimizedPath); } } // Promote the message edge in the LRU. if (isExpired(node)) { expireNode(node, expired, lru); } } else if (node == null) { node = insertNode({}, parent, key, undefined, optimizedPath); } return node; }; },{"100":100,"105":105,"107":107,"110":110,"111":111,"76":76,"78":78,"79":79,"83":83,"85":85,"86":86,"90":90,"97":97}],94:[function(require,module,exports){ var $ref = require(111); var $error = require(110); var getType = require(80); var getSize = require(78); var getTimestamp = require(79); var isExpired = require(85); var isPrimitive = require(92); var isFunction = require(86); var wrapNode = require(107); var expireNode = require(76); var insertNode = require(83); var replaceNode = require(100); var updateNodeAncestors = require(105); var updateBackReferenceVersions = require(104); var reconstructPath = require(97); module.exports = function mergeValueOrInsertBranch( parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var type = getType(node, reference); if (branch || reference) { if (type && isExpired(node)) { type = "expired"; expireNode(node, expired, lru); } if ((type && type !== $ref) || isPrimitive(node)) { node = replaceNode(node, {}, parent, key, lru, replacedPaths); node = insertNode(node, parent, key, version, optimizedPath); node = updateBackReferenceVersions(node, version); } } else { var message = value; var mType = getType(message); // Compare the current cache value with the new value. If either of // them don't have a timestamp, or the message's timestamp is newer, // replace the cache value with the message value. If a comparator // is specified, the comparator takes precedence over timestamps. // // Comparing either Number or undefined to undefined always results in false. var isDistinct = (getTimestamp(message) < getTimestamp(node)) === false; // If at least one of the cache/message are sentinels, compare them. if ((type || mType) && isFunction(comparator)) { isDistinct = !comparator(node, message, optimizedPath.slice(0, optimizedPath.index)); } if (isDistinct) { if (mType === $error && isFunction(errorSelector)) { message = errorSelector(reconstructPath(requestedPath, key), message); mType = message.$type || mType; } message = wrapNode(message, mType, mType ? message.value : message); var sizeOffset = getSize(node) - getSize(message); node = replaceNode(node, message, parent, key, lru, replacedPaths); parent = updateNodeAncestors(parent, sizeOffset, lru, version); node = insertNode(node, parent, key, version, optimizedPath); } } return node; }; },{"100":100,"104":104,"105":105,"107":107,"110":110,"111":111,"76":76,"78":78,"79":79,"80":80,"83":83,"85":85,"86":86,"92":92,"97":97}],95:[function(require,module,exports){ module.exports = function noop() {}; },{}],96:[function(require,module,exports){ module.exports = Date.now; },{}],97:[function(require,module,exports){ /** * Reconstructs the path for the current key, from currentPath (requestedPath) * state maintained during set/merge walk operations. * * During the walk, since the requestedPath array is updated after we attempt to * merge/insert nodes during a walk (it reflects the inserted node's parent branch) * we need to reconstitute a path from it. * * @param {Array} currentPath The current requestedPath state, during the walk * @param {String} key The current key value, during the walk * @return {Array} A new array, with the path which represents the node we're about * to insert */ module.exports = function reconstructPath(currentPath, key) { var path = currentPath.slice(0, currentPath.depth); path[path.length] = key; return path; }; },{}],98:[function(require,module,exports){ var $ref = require(111); var splice = require(42); var isObject = require(90); var unlinkBackReferences = require(102); var unlinkForwardReference = require(103); module.exports = function removeNode(node, parent, key, lru) { if (isObject(node)) { var type = node.$type; if (type) { if (type === $ref) { unlinkForwardReference(node); } splice(lru, node); } unlinkBackReferences(node); // eslint-disable-next-line camelcase parent[key] = node.$_parent = void 0; return true; } return false; }; },{"102":102,"103":103,"111":111,"42":42,"90":90}],99:[function(require,module,exports){ var hasOwn = require(81); var prefix = require(37); var removeNode = require(98); module.exports = function removeNodeAndDescendants(node, parent, key, lru, mergeContext) { if (removeNode(node, parent, key, lru)) { if (node.$type !== undefined && mergeContext && node.$_absolutePath) { mergeContext.hasInvalidatedResult = true; } if (node.$type == null) { for (var key2 in node) { if (key2[0] !== prefix && hasOwn(node, key2)) { removeNodeAndDescendants(node[key2], node, key2, lru, mergeContext); } } } return true; } return false; }; },{"37":37,"81":81,"98":98}],100:[function(require,module,exports){ var isObject = require(90); var transferBackReferences = require(101); var removeNodeAndDescendants = require(99); module.exports = function replaceNode(node, replacement, parent, key, lru, mergeContext) { if (node === replacement) { return node; } else if (isObject(node)) { transferBackReferences(node, replacement); removeNodeAndDescendants(node, parent, key, lru, mergeContext); } parent[key] = replacement; return replacement; }; },{"101":101,"90":90,"99":99}],101:[function(require,module,exports){ var __ref = require(36); module.exports = function transferBackReferences(fromNode, destNode) { // eslint-disable-next-line camelcase var fromNodeRefsLength = fromNode.$_refsLength || 0, // eslint-disable-next-line camelcase destNodeRefsLength = destNode.$_refsLength || 0, i = -1; while (++i < fromNodeRefsLength) { var ref = fromNode[__ref + i]; if (ref !== void 0) { // eslint-disable-next-line camelcase ref.$_context = destNode; destNode[__ref + (destNodeRefsLength + i)] = ref; fromNode[__ref + i] = void 0; } } // eslint-disable-next-line camelcase destNode.$_refsLength = fromNodeRefsLength + destNodeRefsLength; // eslint-disable-next-line camelcase fromNode.$_refsLength = void 0; return destNode; }; },{"36":36}],102:[function(require,module,exports){ var __ref = require(36); module.exports = function unlinkBackReferences(node) { // eslint-disable-next-line camelcase var i = -1, n = node.$_refsLength || 0; while (++i < n) { var ref = node[__ref + i]; if (ref != null) { // eslint-disable-next-line camelcase ref.$_context = ref.$_refIndex = node[__ref + i] = void 0; } } // eslint-disable-next-line camelcase node.$_refsLength = void 0; return node; }; },{"36":36}],103:[function(require,module,exports){ var __ref = require(36); module.exports = function unlinkForwardReference(reference) { // eslint-disable-next-line camelcase var destination = reference.$_context; if (destination) { // eslint-disable-next-line camelcase var i = (reference.$_refIndex || 0) - 1, // eslint-disable-next-line camelcase n = (destination.$_refsLength || 0) - 1; while (++i <= n) { destination[__ref + i] = destination[__ref + (i + 1)]; } // eslint-disable-next-line camelcase destination.$_refsLength = n; // eslint-disable-next-line camelcase reference.$_refIndex = reference.$_context = destination = void 0; } return reference; }; },{"36":36}],104:[function(require,module,exports){ var __ref = require(36); module.exports = function updateBackReferenceVersions(nodeArg, version) { var stack = [nodeArg]; var count = 0; do { var node = stack[count]; // eslint-disable-next-line camelcase if (node && node.$_version !== version) { // eslint-disable-next-line camelcase node.$_version = version; // eslint-disable-next-line camelcase stack[count++] = node.$_parent; var i = -1; // eslint-disable-next-line camelcase var n = node.$_refsLength || 0; while (++i < n) { stack[count++] = node[__ref + i]; } } } while (--count > -1); return nodeArg; }; },{"36":36}],105:[function(require,module,exports){ var removeNode = require(98); var updateBackReferenceVersions = require(104); module.exports = function updateNodeAncestors(nodeArg, offset, lru, version) { var child = nodeArg; do { var node = child.$_parent; var size = child.$size = (child.$size || 0) - offset; if (size <= 0 && node != null) { removeNode(child, node, child.$_key, lru); } else if (child.$_version !== version) { updateBackReferenceVersions(child, version); } child = node; } while (child); return nodeArg; }; },{"104":104,"98":98}],106:[function(require,module,exports){ var isArray = Array.isArray; var isPathValue = require(91); var isJSONGraphEnvelope = require(89); var isJSONEnvelope = require(88); var pathSyntax = require(125); /** * * @param {Object} allowedInput - allowedInput is a map of input styles * that are allowed * @private */ module.exports = function validateInput(args, allowedInput, method) { for (var i = 0, len = args.length; i < len; ++i) { var arg = args[i]; var valid = false; // Path if (isArray(arg) && allowedInput.path) { valid = true; } // Path Syntax else if (typeof arg === "string" && allowedInput.pathSyntax) { try { pathSyntax.fromPath(arg); valid = true; } catch (errorMessage) { return new Error("Path syntax validation error -- " + errorMessage); } } // Path Value else if (isPathValue(arg) && allowedInput.pathValue) { try { arg.path = pathSyntax.fromPath(arg.path); valid = true; } catch (errorMessage) { return new Error("Path syntax validation error -- " + errorMessage); } } // jsonGraph {jsonGraph: { ... }, paths: [ ... ]} else if (isJSONGraphEnvelope(arg) && allowedInput.jsonGraph) { valid = true; } // json env {json: {...}} else if (isJSONEnvelope(arg) && allowedInput.json) { valid = true; } // selector functions else if (typeof arg === "function" && i + 1 === len && allowedInput.selector) { valid = true; } if (!valid) { return new Error("Unrecognized argument " + (typeof arg) + " [" + String(arg) + "] " + "to Model#" + method + ""); } } return true; }; },{"125":125,"88":88,"89":89,"91":91}],107:[function(require,module,exports){ var now = require(96); var expiresNow = require(113); var atomSize = 50; var clone = require(73); var isArray = Array.isArray; var getSize = require(78); var getExpires = require(77); var atomType = require(109); module.exports = function wrapNode(nodeArg, typeArg, value) { var size = 0; var node = nodeArg; var type = typeArg; if (type) { var modelCreated = node.$_modelCreated; node = clone(node); size = getSize(node); node.$type = type; // eslint-disable-next-line camelcase node.$_prev = undefined; // eslint-disable-next-line camelcase node.$_next = undefined; // eslint-disable-next-line camelcase node.$_modelCreated = modelCreated || false; } else { node = { $type: atomType, value: value, // eslint-disable-next-line camelcase $_prev: undefined, // eslint-disable-next-line camelcase $_next: undefined, // eslint-disable-next-line camelcase $_modelCreated: true }; } if (value == null) { size = atomSize + 1; } else if (size == null || size <= 0) { switch (typeof value) { case "object": if (isArray(value)) { size = atomSize + value.length; } else { size = atomSize + 1; } break; case "string": size = atomSize + value.length; break; default: size = atomSize + 1; break; } } var expires = getExpires(node); if (typeof expires === "number" && expires < expiresNow) { node.$expires = now() + (expires * -1); } node.$size = size; return node; }; },{"109":109,"113":113,"73":73,"77":77,"78":78,"96":96}],108:[function(require,module,exports){ /** * FromEsObserverAdapter is an adpater from an ES Observer to an Rx 2 Observer * @constructor FromEsObserverAdapter */ function FromEsObserverAdapter(esObserver) { this.esObserver = esObserver; } FromEsObserverAdapter.prototype = { onNext: function onNext(value) { if (typeof this.esObserver.next === "function") { this.esObserver.next(value); } }, onError: function onError(error) { if (typeof this.esObserver.error === "function") { this.esObserver.error(error); } }, onCompleted: function onCompleted() { if (typeof this.esObserver.complete === "function") { this.esObserver.complete(); } } }; /** * ToEsSubscriptionAdapter is an adpater from the Rx 2 subscription to the ES subscription * @constructor ToEsSubscriptionAdapter */ function ToEsSubscriptionAdapter(subscription) { this.subscription = subscription; } ToEsSubscriptionAdapter.prototype.unsubscribe = function unsubscribe() { this.subscription.dispose(); }; function toEsObservable(_self) { return { subscribe: function subscribe(observer) { return new ToEsSubscriptionAdapter(_self.subscribe(new FromEsObserverAdapter(observer))); } }; } module.exports = toEsObservable; },{}],109:[function(require,module,exports){ module.exports = "atom"; },{}],110:[function(require,module,exports){ module.exports = "error"; },{}],111:[function(require,module,exports){ module.exports = "ref"; },{}],112:[function(require,module,exports){ module.exports = 1; },{}],113:[function(require,module,exports){ module.exports = 0; },{}],114:[function(require,module,exports){ "use strict"; // rawAsap provides everything we need except exception management. var rawAsap = require(115); // RawTasks are recycled to reduce GC churn. var freeTasks = []; // We queue errors to ensure they are thrown in right order (FIFO). // Array-as-queue is good enough here, since we are just dealing with exceptions. var pendingErrors = []; var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError); function throwFirstError() { if (pendingErrors.length) { throw pendingErrors.shift(); } } /** * Calls a task as soon as possible after returning, in its own event, with priority * over other events like animation, reflow, and repaint. An error thrown from an * event will not interrupt, nor even substantially slow down the processing of * other events, but will be rather postponed to a lower priority event. * @param {{call}} task A callable object, typically a function that takes no * arguments. */ module.exports = asap; function asap(task) { var rawTask; if (freeTasks.length) { rawTask = freeTasks.pop(); } else { rawTask = new RawTask(); } rawTask.task = task; rawAsap(rawTask); } // We wrap tasks with recyclable task objects. A task object implements // `call`, just like a function. function RawTask() { this.task = null; } // The sole purpose of wrapping the task is to catch the exception and recycle // the task object after its single use. RawTask.prototype.call = function () { try { this.task.call(); } catch (error) { if (asap.onerror) { // This hook exists purely for testing purposes. // Its name will be periodically randomized to break any code that // depends on its existence. asap.onerror(error); } else { // In a web browser, exceptions are not fatal. However, to avoid // slowing down the queue of pending tasks, we rethrow the error in a // lower priority turn. pendingErrors.push(error); requestErrorThrow(); } } finally { this.task = null; freeTasks[freeTasks.length] = this; } }; },{"115":115}],115:[function(require,module,exports){ (function (global){(function (){ "use strict"; // Use the fastest means possible to execute a task in its own turn, with // priority over other events including IO, animation, reflow, and redraw // events in browsers. // // An exception thrown by a task will permanently interrupt the processing of // subsequent tasks. The higher level `asap` function ensures that if an // exception is thrown by a task, that the task queue will continue flushing as // soon as possible, but if you use `rawAsap` directly, you are responsible to // either ensure that no exceptions are thrown from your task, or to manually // call `rawAsap.requestFlush` if an exception is thrown. module.exports = rawAsap; function rawAsap(task) { if (!queue.length) { requestFlush(); flushing = true; } // Equivalent to push, but avoids a function call. queue[queue.length] = task; } var queue = []; // Once a flush has been requested, no further calls to `requestFlush` are // necessary until the next `flush` completes. var flushing = false; // `requestFlush` is an implementation-specific method that attempts to kick // off a `flush` event as quickly as possible. `flush` will attempt to exhaust // the event queue before yielding to the browser's own event loop. var requestFlush; // The position of the next task to execute in the task queue. This is // preserved between calls to `flush` so that it can be resumed if // a task throws an exception. var index = 0; // If a task schedules additional tasks recursively, the task queue can grow // unbounded. To prevent memory exhaustion, the task queue will periodically // truncate already-completed tasks. var capacity = 1024; // The flush function processes all tasks that have been scheduled with // `rawAsap` unless and until one of those tasks throws an exception. // If a task throws an exception, `flush` ensures that its state will remain // consistent and will resume where it left off when called again. // However, `flush` does not make any arrangements to be called again if an // exception is thrown. function flush() { while (index < queue.length) { var currentIndex = index; // Advance the index before calling the task. This ensures that we will // begin flushing on the next task the task throws an error. index = index + 1; queue[currentIndex].call(); // Prevent leaking memory for long chains of recursive calls to `asap`. // If we call `asap` within tasks scheduled by `asap`, the queue will // grow, but to avoid an O(n) walk for every task we execute, we don't // shift tasks off the queue after they have been executed. // Instead, we periodically shift 1024 tasks off the queue. if (index > capacity) { // Manually shift all values starting at the index back to the // beginning of the queue. for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) { queue[scan] = queue[scan + index]; } queue.length -= index; index = 0; } } queue.length = 0; index = 0; flushing = false; } // `requestFlush` is implemented using a strategy based on data collected from // every available SauceLabs Selenium web driver worker at time of writing. // https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593 // Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that // have WebKitMutationObserver but not un-prefixed MutationObserver. // Must use `global` or `self` instead of `window` to work in both frames and web // workers. `global` is a provision of Browserify, Mr, Mrs, or Mop. /* globals self */ var scope = typeof global !== "undefined" ? global : self; var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; // MutationObservers are desirable because they have high priority and work // reliably everywhere they are implemented. // They are implemented in all modern browsers. // // - Android 4-4.3 // - Chrome 26-34 // - Firefox 14-29 // - Internet Explorer 11 // - iPad Safari 6-7.1 // - iPhone Safari 7-7.1 // - Safari 6-7 if (typeof BrowserMutationObserver === "function") { requestFlush = makeRequestCallFromMutationObserver(flush); // MessageChannels are desirable because they give direct access to the HTML // task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera // 11-12, and in web workers in many engines. // Although message channels yield to any queued rendering and IO tasks, they // would be better than imposing the 4ms delay of timers. // However, they do not work reliably in Internet Explorer or Safari. // Internet Explorer 10 is the only browser that has setImmediate but does // not have MutationObservers. // Although setImmediate yields to the browser's renderer, it would be // preferrable to falling back to setTimeout since it does not have // the minimum 4ms penalty. // Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and // Desktop to a lesser extent) that renders both setImmediate and // MessageChannel useless for the purposes of ASAP. // https://github.com/kriskowal/q/issues/396 // Timers are implemented universally. // We fall back to timers in workers in most engines, and in foreground // contexts in the following browsers. // However, note that even this simple case requires nuances to operate in a // broad spectrum of browsers. // // - Firefox 3-13 // - Internet Explorer 6-9 // - iPad Safari 4.3 // - Lynx 2.8.7 } else { requestFlush = makeRequestCallFromTimer(flush); } // `requestFlush` requests that the high priority event queue be flushed as // soon as possible. // This is useful to prevent an error thrown in a task from stalling the event // queue if the exception handled by Node.js’s // `process.on("uncaughtException")` or by a domain. rawAsap.requestFlush = requestFlush; // To request a high priority event, we induce a mutation observer by toggling // the text of a text node between "1" and "-1". function makeRequestCallFromMutationObserver(callback) { var toggle = 1; var observer = new BrowserMutationObserver(callback); var node = document.createTextNode(""); observer.observe(node, {characterData: true}); return function requestCall() { toggle = -toggle; node.data = toggle; }; } // The message channel technique was discovered by Malte Ubl and was the // original foundation for this library. // http://www.nonblocking.io/2011/06/windownexttick.html // Safari 6.0.5 (at least) intermittently fails to create message ports on a // page's first load. Thankfully, this version of Safari supports // MutationObservers, so we don't need to fall back in that case. // function makeRequestCallFromMessageChannel(callback) { // var channel = new MessageChannel(); // channel.port1.onmessage = callback; // return function requestCall() { // channel.port2.postMessage(0); // }; // } // For reasons explained above, we are also unable to use `setImmediate` // under any circumstances. // Even if we were, there is another bug in Internet Explorer 10. // It is not sufficient to assign `setImmediate` to `requestFlush` because // `setImmediate` must be called *by name* and therefore must be wrapped in a // closure. // Never forget. // function makeRequestCallFromSetImmediate(callback) { // return function requestCall() { // setImmediate(callback); // }; // } // Safari 6.0 has a problem where timers will get lost while the user is // scrolling. This problem does not impact ASAP because Safari 6.0 supports // mutation observers, so that implementation is used instead. // However, if we ever elect to use timers in Safari, the prevalent work-around // is to add a scroll event listener that calls for a flush. // `setTimeout` does not call the passed callback if the delay is less than // approximately 7 in web workers in Firefox 8 through 18, and sometimes not // even then. function makeRequestCallFromTimer(callback) { return function requestCall() { // We dispatch a timeout with a specified delay of 0 for engines that // can reliably accommodate that request. This will usually be snapped // to a 4 milisecond delay, but once we're flushing, there's no delay // between events. var timeoutHandle = setTimeout(handleTimer, 0); // However, since this timer gets frequently dropped in Firefox // workers, we enlist an interval handle that will try to fire // an event 20 times per second until it succeeds. var intervalHandle = setInterval(handleTimer, 50); function handleTimer() { // Whichever timer succeeds will cancel both timers and // execute the callback. clearTimeout(timeoutHandle); clearInterval(intervalHandle); callback(); } }; } // This is for `asap.js` only. // Its name will be periodically randomized to break any code that depends on // its existence. rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer; // ASAP was originally a nextTick shim included in Q. This was factored out // into this ASAP package. It was later adapted to RSVP which made further // amendments. These decisions, particularly to marginalize MessageChannel and // to capture the MutationObserver implementation in a closure, were integrated // back into ASAP proper. // https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],116:[function(require,module,exports){ 'use strict'; var request = require(120); var buildQueryObject = require(117); var isArray = Array.isArray; function simpleExtend(obj, obj2) { var prop; for (prop in obj2) { obj[prop] = obj2[prop]; } return obj; } function XMLHttpSource(jsongUrl, config) { this._jsongUrl = jsongUrl; if (typeof config === 'number') { var newConfig = { timeout: config }; config = newConfig; } this._config = simpleExtend({ timeout: 15000, headers: {} }, config || {}); } XMLHttpSource.prototype = { // because javascript constructor: XMLHttpSource, /** * buildQueryObject helper */ buildQueryObject: buildQueryObject, /** * @inheritDoc DataSource#get */ get: function httpSourceGet(pathSet) { var method = 'GET'; var queryObject = this.buildQueryObject(this._jsongUrl, method, { paths: pathSet, method: 'get' }); var config = simpleExtend(queryObject, this._config); // pass context for onBeforeRequest callback var context = this; return request(method, config, context); }, /** * @inheritDoc DataSource#set */ set: function httpSourceSet(jsongEnv) { var method = 'POST'; var queryObject = this.buildQueryObject(this._jsongUrl, method, { jsonGraph: jsongEnv, method: 'set' }); var config = simpleExtend(queryObject, this._config); config.headers["Content-Type"] = "application/x-www-form-urlencoded"; // pass context for onBeforeRequest callback var context = this; return request(method, config, context); }, /** * @inheritDoc DataSource#call */ call: function httpSourceCall(callPath, args, pathSuffix, paths) { // arguments defaults args = args || []; pathSuffix = pathSuffix || []; paths = paths || []; var method = 'POST'; var queryData = []; queryData.push('method=call'); queryData.push('callPath=' + encodeURIComponent(JSON.stringify(callPath))); queryData.push('arguments=' + encodeURIComponent(JSON.stringify(args))); queryData.push('pathSuffixes=' + encodeURIComponent(JSON.stringify(pathSuffix))); queryData.push('paths=' + encodeURIComponent(JSON.stringify(paths))); var queryObject = this.buildQueryObject(this._jsongUrl, method, queryData.join('&')); var config = simpleExtend(queryObject, this._config); config.headers["Content-Type"] = "application/x-www-form-urlencoded"; // pass context for onBeforeRequest callback var context = this; return request(method, config, context); } }; // ES6 modules XMLHttpSource.XMLHttpSource = XMLHttpSource; XMLHttpSource['default'] = XMLHttpSource; // commonjs module.exports = XMLHttpSource; },{"117":117,"120":120}],117:[function(require,module,exports){ 'use strict'; module.exports = function buildQueryObject(url, method, queryData) { var qData = []; var keys; var data = {url: url}; var isQueryParamUrl = url.indexOf('?') !== -1; var startUrl = (isQueryParamUrl) ? '&' : '?'; if (typeof queryData === 'string') { qData.push(queryData); } else { keys = Object.keys(queryData); keys.forEach(function (k) { var value = (typeof queryData[k] === 'object') ? JSON.stringify(queryData[k]) : queryData[k]; qData.push(k + '=' + encodeURIComponent(value)); }); } if (method === 'GET') { data.url += startUrl + qData.join('&'); } else { data.data = qData.join('&'); } return data; }; },{}],118:[function(require,module,exports){ (function (global){(function (){ 'use strict'; // Get CORS support even for older IE module.exports = function getCORSRequest() { var xhr = new global.XMLHttpRequest(); if ('withCredentials' in xhr) { return xhr; } else if (!!global.XDomainRequest) { return new XDomainRequest(); } else { throw new Error('CORS is not supported by your browser'); } }; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],119:[function(require,module,exports){ (function (global){(function (){ 'use strict'; module.exports = function getXMLHttpRequest() { var progId, progIds, i; if (global.XMLHttpRequest) { return new global.XMLHttpRequest(); } else { try { progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; for (i = 0; i < 3; i++) { try { progId = progIds[i]; if (new global.ActiveXObject(progId)) { break; } } catch(e) { } } return new global.ActiveXObject(progId); } catch (e) { throw new Error('XMLHttpRequest is not supported by your browser'); } } }; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],120:[function(require,module,exports){ 'use strict'; var getXMLHttpRequest = require(119); var getCORSRequest = require(118); var hasOwnProp = Object.prototype.hasOwnProperty; var noop = function() {}; function Observable() {} Observable.create = function(subscribe) { var o = new Observable(); o.subscribe = function(onNext, onError, onCompleted) { var observer; var disposable; if (typeof onNext === 'function') { observer = { onNext: onNext, onError: (onError || noop), onCompleted: (onCompleted || noop) }; } else { observer = onNext; } disposable = subscribe(observer); if (typeof disposable === 'function') { return { dispose: disposable }; } else { return disposable; } }; return o; }; function request(method, options, context) { return Observable.create(function requestObserver(observer) { var config = { method: method || 'GET', crossDomain: false, async: true, headers: {}, responseType: 'json' }; var xhr, isDone, headers, header, prop; for (prop in options) { if (hasOwnProp.call(options, prop)) { config[prop] = options[prop]; } } // Add request with Headers if (!config.crossDomain && !config.headers['X-Requested-With']) { config.headers['X-Requested-With'] = 'XMLHttpRequest'; } // allow the user to mutate the config open if (context.onBeforeRequest != null) { context.onBeforeRequest(config); } // create xhr try { xhr = config.crossDomain ? getCORSRequest() : getXMLHttpRequest(); } catch (err) { observer.onError(err); } try { // Takes the url and opens the connection if (config.user) { xhr.open(config.method, config.url, config.async, config.user, config.password); } else { xhr.open(config.method, config.url, config.async); } // Sets timeout information xhr.timeout = config.timeout; // Anything but explicit false results in true. xhr.withCredentials = config.withCredentials !== false; // Fills the request headers headers = config.headers; for (header in headers) { if (hasOwnProp.call(headers, header)) { xhr.setRequestHeader(header, headers[header]); } } if (config.responseType) { try { xhr.responseType = config.responseType; } catch (e) { // WebKit added support for the json responseType value on 09/03/2013 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are // known to throw when setting the value "json" as the response type. Other older // browsers implementing the responseType // // The json response type can be ignored if not supported, because JSON payloads are // parsed on the client-side regardless. if (config.responseType !== 'json') { throw e; } } } xhr.onreadystatechange = function onreadystatechange(e) { // Complete if (xhr.readyState === 4) { if (!isDone) { isDone = true; onXhrLoad(observer, xhr, e); } } }; // Timeout xhr.ontimeout = function ontimeout(e) { if (!isDone) { isDone = true; onXhrError(observer, xhr, 'timeout error', e); } }; // Send Request xhr.send(config.data); } catch (e) { observer.onError(e); } // Dispose return function dispose() { // Doesn't work in IE9 if (!isDone && xhr.readyState !== 4) { isDone = true; xhr.abort(); } };//Dispose }); } /* * General handling of ultimate failure (after appropriate retries) */ function _handleXhrError(observer, textStatus, errorThrown) { // IE9: cross-domain request may be considered errors if (!errorThrown) { errorThrown = new Error(textStatus); } observer.onError(errorThrown); } function onXhrLoad(observer, xhr, e) { var responseData, responseObject, responseType; // If there's no observer, the request has been (or is being) cancelled. if (xhr && observer) { responseType = xhr.responseType; // responseText is the old-school way of retrieving response (supported by IE8 & 9) // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) responseData = ('response' in xhr) ? xhr.response : xhr.responseText; // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) var status = (xhr.status === 1223) ? 204 : xhr.status; if (status >= 200 && status <= 399) { try { if (responseType !== 'json') { responseData = JSON.parse(responseData || ''); } if (typeof responseData === 'string') { responseData = JSON.parse(responseData || ''); } } catch (e) { _handleXhrError(observer, 'invalid json', e); } observer.onNext(responseData); observer.onCompleted(); return; } else if (status === 401 || status === 403 || status === 407) { return _handleXhrError(observer, responseData); } else if (status === 410) { // TODO: Retry ? return _handleXhrError(observer, responseData); } else if (status === 408 || status === 504) { // TODO: Retry ? return _handleXhrError(observer, responseData); } else { return _handleXhrError(observer, responseData || ('Response code ' + status)); }//if }//if }//onXhrLoad function onXhrError(observer, xhr, status, e) { _handleXhrError(observer, status || xhr.statusText || 'request error', e); } module.exports = request; },{"118":118,"119":119}],121:[function(require,module,exports){ var pathSyntax = require(125); function sentinel(type, value, props) { var copy = Object.create(null); if (props != null) { for(var key in props) { copy[key] = props[key]; } copy["$type"] = type; copy.value = value; return copy; } else { return { $type: type, value: value }; } } module.exports = { ref: function ref(path, props) { return sentinel("ref", pathSyntax.fromPath(path), props); }, atom: function atom(value, props) { return sentinel("atom", value, props); }, undefined: function() { return sentinel("atom"); }, error: function error(errorValue, props) { return sentinel("error", errorValue, props); }, pathValue: function pathValue(path, value) { return { path: pathSyntax.fromPath(path), value: value }; }, pathInvalidation: function pathInvalidation(path) { return { path: pathSyntax.fromPath(path), invalidated: true }; } }; },{"125":125}],122:[function(require,module,exports){ module.exports = { integers: 'integers', ranges: 'ranges', keys: 'keys' }; },{}],123:[function(require,module,exports){ var TokenTypes = { token: 'token', dotSeparator: '.', commaSeparator: ',', openingBracket: '[', closingBracket: ']', openingBrace: '{', closingBrace: '}', escape: '\\', space: ' ', colon: ':', quote: 'quote', unknown: 'unknown' }; module.exports = TokenTypes; },{}],124:[function(require,module,exports){ module.exports = { indexer: { nested: 'Indexers cannot be nested.', needQuotes: 'unquoted indexers must be numeric.', empty: 'cannot have empty indexers.', leadingDot: 'Indexers cannot have leading dots.', leadingComma: 'Indexers cannot have leading comma.', requiresComma: 'Indexers require commas between indexer args.', routedTokens: 'Only one token can be used per indexer when specifying routed tokens.' }, range: { precedingNaN: 'ranges must be preceded by numbers.', suceedingNaN: 'ranges must be suceeded by numbers.' }, routed: { invalid: 'Invalid routed token. only integers|ranges|keys are supported.' }, quote: { empty: 'cannot have empty quoted keys.', illegalEscape: 'Invalid escape character. Only quotes are escapable.' }, unexpectedToken: 'Unexpected token.', invalidIdentifier: 'Invalid Identifier.', invalidPath: 'Please provide a valid path.', throwError: function(err, tokenizer, token) { if (token) { throw err + ' -- ' + tokenizer.parseString + ' with next token: ' + token; } throw err + ' -- ' + tokenizer.parseString; } }; },{}],125:[function(require,module,exports){ var Tokenizer = require(131); var head = require(126); var RoutedTokens = require(122); var parser = function parser(string, extendedRules) { return head(new Tokenizer(string, extendedRules)); }; module.exports = parser; // Constructs the paths from paths / pathValues that have strings. // If it does not have a string, just moves the value into the return // results. parser.fromPathsOrPathValues = function(paths, ext) { if (!paths) { return []; } var out = []; for (var i = 0, len = paths.length; i < len; i++) { // Is the path a string if (typeof paths[i] === 'string') { out[i] = parser(paths[i], ext); } // is the path a path value with a string value. else if (typeof paths[i].path === 'string') { out[i] = { path: parser(paths[i].path, ext), value: paths[i].value }; } // just copy it over. else { out[i] = paths[i]; } } return out; }; // If the argument is a string, this with convert, else just return // the path provided. parser.fromPath = function(path, ext) { if (!path) { return []; } if (typeof path === 'string') { return parser(path, ext); } return path; }; // Potential routed tokens. parser.RoutedTokens = RoutedTokens; },{"122":122,"126":126,"131":131}],126:[function(require,module,exports){ var TokenTypes = require(123); var E = require(124); var indexer = require(127); /** * The top level of the parse tree. This returns the generated path * from the tokenizer. */ module.exports = function head(tokenizer) { var token = tokenizer.next(); var state = {}; var out = []; while (!token.done) { switch (token.type) { case TokenTypes.token: var first = +token.token[0]; if (!isNaN(first)) { E.throwError(E.invalidIdentifier, tokenizer); } out[out.length] = token.token; break; // dotSeparators at the top level have no meaning case TokenTypes.dotSeparator: if (out.length === 0) { E.throwError(E.unexpectedToken, tokenizer); } break; // Spaces do nothing. case TokenTypes.space: // NOTE: Spaces at the top level are allowed. // titlesById .summary is a valid path. break; // Its time to decend the parse tree. case TokenTypes.openingBracket: indexer(tokenizer, token, state, out); break; default: E.throwError(E.unexpectedToken, tokenizer); break; } // Keep cycling through the tokenizer. token = tokenizer.next(); } if (out.length === 0) { E.throwError(E.invalidPath, tokenizer); } return out; }; },{"123":123,"124":124,"127":127}],127:[function(require,module,exports){ var TokenTypes = require(123); var E = require(124); var idxE = E.indexer; var range = require(129); var quote = require(128); var routed = require(130); /** * The indexer is all the logic that happens in between * the '[', opening bracket, and ']' closing bracket. */ module.exports = function indexer(tokenizer, openingToken, state, out) { var token = tokenizer.next(); var done = false; var allowedMaxLength = 1; var routedIndexer = false; // State variables state.indexer = []; while (!token.done) { switch (token.type) { case TokenTypes.token: case TokenTypes.quote: // ensures that token adders are properly delimited. if (state.indexer.length === allowedMaxLength) { E.throwError(idxE.requiresComma, tokenizer); } break; } switch (token.type) { // Extended syntax case case TokenTypes.openingBrace: routedIndexer = true; routed(tokenizer, token, state, out); break; case TokenTypes.token: var t = +token.token; if (isNaN(t)) { E.throwError(idxE.needQuotes, tokenizer); } state.indexer[state.indexer.length] = t; break; // dotSeparators at the top level have no meaning case TokenTypes.dotSeparator: if (!state.indexer.length) { E.throwError(idxE.leadingDot, tokenizer); } range(tokenizer, token, state, out); break; // Spaces do nothing. case TokenTypes.space: break; case TokenTypes.closingBracket: done = true; break; // The quotes require their own tree due to what can be in it. case TokenTypes.quote: quote(tokenizer, token, state, out); break; // Its time to decend the parse tree. case TokenTypes.openingBracket: E.throwError(idxE.nested, tokenizer); break; case TokenTypes.commaSeparator: ++allowedMaxLength; break; default: E.throwError(E.unexpectedToken, tokenizer); break; } // If done, leave loop if (done) { break; } // Keep cycling through the tokenizer. token = tokenizer.next(); } if (state.indexer.length === 0) { E.throwError(idxE.empty, tokenizer); } if (state.indexer.length > 1 && routedIndexer) { E.throwError(idxE.routedTokens, tokenizer); } // Remember, if an array of 1, keySets will be generated. if (state.indexer.length === 1) { state.indexer = state.indexer[0]; } out[out.length] = state.indexer; // Clean state. state.indexer = undefined; }; },{"123":123,"124":124,"128":128,"129":129,"130":130}],128:[function(require,module,exports){ var TokenTypes = require(123); var E = require(124); var quoteE = E.quote; /** * quote is all the parse tree in between quotes. This includes the only * escaping logic. * * parse-tree: * (.|())* */ module.exports = function quote(tokenizer, openingToken, state, out) { var token = tokenizer.next(); var innerToken = ''; var openingQuote = openingToken.token; var escaping = false; var done = false; while (!token.done) { switch (token.type) { case TokenTypes.token: case TokenTypes.space: case TokenTypes.dotSeparator: case TokenTypes.commaSeparator: case TokenTypes.openingBracket: case TokenTypes.closingBracket: case TokenTypes.openingBrace: case TokenTypes.closingBrace: if (escaping) { E.throwError(quoteE.illegalEscape, tokenizer); } innerToken += token.token; break; case TokenTypes.quote: // the simple case. We are escaping if (escaping) { innerToken += token.token; escaping = false; } // its not a quote that is the opening quote else if (token.token !== openingQuote) { innerToken += token.token; } // last thing left. Its a quote that is the opening quote // therefore we must produce the inner token of the indexer. else { done = true; } break; case TokenTypes.escape: escaping = true; break; default: E.throwError(E.unexpectedToken, tokenizer); } // If done, leave loop if (done) { break; } // Keep cycling through the tokenizer. token = tokenizer.next(); } if (innerToken.length === 0) { E.throwError(quoteE.empty, tokenizer); } state.indexer[state.indexer.length] = innerToken; }; },{"123":123,"124":124}],129:[function(require,module,exports){ var Tokenizer = require(131); var TokenTypes = require(123); var E = require(124); /** * The indexer is all the logic that happens in between * the '[', opening bracket, and ']' closing bracket. */ module.exports = function range(tokenizer, openingToken, state, out) { var token = tokenizer.peek(); var dotCount = 1; var done = false; var inclusive = true; // Grab the last token off the stack. Must be an integer. var idx = state.indexer.length - 1; var from = Tokenizer.toNumber(state.indexer[idx]); var to; if (isNaN(from)) { E.throwError(E.range.precedingNaN, tokenizer); } // Why is number checking so difficult in javascript. while (!done && !token.done) { switch (token.type) { // dotSeparators at the top level have no meaning case TokenTypes.dotSeparator: if (dotCount === 3) { E.throwError(E.unexpectedToken, tokenizer); } ++dotCount; if (dotCount === 3) { inclusive = false; } break; case TokenTypes.token: // move the tokenizer forward and save to. to = Tokenizer.toNumber(tokenizer.next().token); // throw potential error. if (isNaN(to)) { E.throwError(E.range.suceedingNaN, tokenizer); } done = true; break; default: done = true; break; } // Keep cycling through the tokenizer. But ranges have to peek // before they go to the next token since there is no 'terminating' // character. if (!done) { tokenizer.next(); // go to the next token without consuming. token = tokenizer.peek(); } // break and remove state information. else { break; } } state.indexer[idx] = {from: from, to: inclusive ? to : to - 1}; }; },{"123":123,"124":124,"131":131}],130:[function(require,module,exports){ var TokenTypes = require(123); var RoutedTokens = require(122); var E = require(124); var routedE = E.routed; /** * The routing logic. * * parse-tree: * (:) */ module.exports = function routed(tokenizer, openingToken, state, out) { var routeToken = tokenizer.next(); var named = false; var name = ''; // ensure the routed token is a valid ident. switch (routeToken.token) { case RoutedTokens.integers: case RoutedTokens.ranges: case RoutedTokens.keys: //valid break; default: E.throwError(routedE.invalid, tokenizer); break; } // Now its time for colon or ending brace. var next = tokenizer.next(); // we are parsing a named identifier. if (next.type === TokenTypes.colon) { named = true; // Get the token name. next = tokenizer.next(); if (next.type !== TokenTypes.token) { E.throwError(routedE.invalid, tokenizer); } name = next.token; // move to the closing brace. next = tokenizer.next(); } // must close with a brace. if (next.type === TokenTypes.closingBrace) { var outputToken = { type: routeToken.token, named: named, name: name }; state.indexer[state.indexer.length] = outputToken; } // closing brace expected else { E.throwError(routedE.invalid, tokenizer); } }; },{"122":122,"123":123,"124":124}],131:[function(require,module,exports){ var TokenTypes = require(123); var DOT_SEPARATOR = '.'; var COMMA_SEPARATOR = ','; var OPENING_BRACKET = '['; var CLOSING_BRACKET = ']'; var OPENING_BRACE = '{'; var CLOSING_BRACE = '}'; var COLON = ':'; var ESCAPE = '\\'; var DOUBLE_OUOTES = '"'; var SINGE_OUOTES = "'"; var SPACE = " "; var SPECIAL_CHARACTERS = '\\\'"[]., '; var EXT_SPECIAL_CHARACTERS = '\\{}\'"[]., :'; var Tokenizer = module.exports = function(string, ext) { this._string = string; this._idx = -1; this._extended = ext; this.parseString = ''; }; Tokenizer.prototype = { /** * grabs the next token either from the peek operation or generates the * next token. */ next: function() { var nextToken = this._nextToken ? this._nextToken : getNext(this._string, this._idx, this._extended); this._idx = nextToken.idx; this._nextToken = false; this.parseString += nextToken.token.token; return nextToken.token; }, /** * will peak but not increment the tokenizer */ peek: function() { var nextToken = this._nextToken ? this._nextToken : getNext(this._string, this._idx, this._extended); this._nextToken = nextToken; return nextToken.token; } }; Tokenizer.toNumber = function toNumber(x) { if (!isNaN(+x)) { return +x; } return NaN; }; function toOutput(token, type, done) { return { token: token, done: done, type: type }; } function getNext(string, idx, ext) { var output = false; var token = ''; var specialChars = ext ? EXT_SPECIAL_CHARACTERS : SPECIAL_CHARACTERS; var done; do { done = idx + 1 >= string.length; if (done) { break; } // we have to peek at the next token var character = string[idx + 1]; if (character !== undefined && specialChars.indexOf(character) === -1) { token += character; ++idx; continue; } // The token to delimiting character transition. else if (token.length) { break; } ++idx; var type; switch (character) { case DOT_SEPARATOR: type = TokenTypes.dotSeparator; break; case COMMA_SEPARATOR: type = TokenTypes.commaSeparator; break; case OPENING_BRACKET: type = TokenTypes.openingBracket; break; case CLOSING_BRACKET: type = TokenTypes.closingBracket; break; case OPENING_BRACE: type = TokenTypes.openingBrace; break; case CLOSING_BRACE: type = TokenTypes.closingBrace; break; case SPACE: type = TokenTypes.space; break; case DOUBLE_OUOTES: case SINGE_OUOTES: type = TokenTypes.quote; break; case ESCAPE: type = TokenTypes.escape; break; case COLON: type = TokenTypes.colon; break; default: type = TokenTypes.unknown; break; } output = toOutput(character, type, false); break; } while (!done); if (!output && token.length) { output = toOutput(token, TokenTypes.token, false); } if (!output) { output = {done: true}; } return { token: output, idx: idx }; } },{"123":123}],132:[function(require,module,exports){ var toPaths = require(148); var toTree = require(149); module.exports = function collapse(paths) { var collapseMap = paths. reduce(function(acc, path) { var len = path.length; if (!acc[len]) { acc[len] = []; } acc[len].push(path); return acc; }, {}); Object. keys(collapseMap). forEach(function(collapseKey) { collapseMap[collapseKey] = toTree(collapseMap[collapseKey]); }); return toPaths(collapseMap); }; },{"148":148,"149":149}],133:[function(require,module,exports){ /*eslint-disable*/ module.exports = { innerReferences: 'References with inner references are not allowed.', circularReference: 'There appears to be a circular reference, maximum reference following exceeded.' }; },{}],134:[function(require,module,exports){ /** * Escapes a string by prefixing it with "_". This function should be used on * untrusted input before it is embedded into paths. The goal is to ensure that * no reserved words (ex. "$type") make their way into paths and consequently * JSON Graph objects. */ module.exports = function escape(str) { return "_" + str; }; },{}],135:[function(require,module,exports){ var errors = require(133); /** * performs the simplified cache reference follow. This * differs from get as there is just following and reporting, * not much else. * * @param {Object} cacheRoot * @param {Array} ref */ function followReference(cacheRoot, ref, maxRefFollow) { if (typeof maxRefFollow === "undefined") { maxRefFollow = 5; } var branch = cacheRoot; var node = branch; var refPath = ref; var depth = -1; var referenceCount = 0; while (++depth < refPath.length) { var key = refPath[depth]; node = branch[key]; if ( node === null || typeof node !== "object" || (node.$type && node.$type !== "ref") ) { break; } if (node.$type === "ref") { // Show stopper exception. This route is malformed. if (depth + 1 < refPath.length) { return { error: new Error(errors.innerReferences) }; } if (referenceCount >= maxRefFollow) { return { error: new Error(errors.circularReference) }; } refPath = node.value; depth = -1; branch = cacheRoot; referenceCount++; } else { branch = node; } } return { node: node, refPath: refPath }; } module.exports = followReference; },{"133":133}],136:[function(require,module,exports){ var iterateKeySet = require(139); /** * Tests to see if the intersection should be stripped from the * total paths. The only way this happens currently is if the entirety * of the path is contained in the tree. * @private */ module.exports = function hasIntersection(tree, path, depth) { var current = tree; var intersects = true; // Continue iteratively going down a path until a complex key is // encountered, then recurse. for (;intersects && depth < path.length; ++depth) { var key = path[depth]; var keyType = typeof key; // We have to iterate key set if (key && keyType === 'object') { var note = {}; var innerKey = iterateKeySet(key, note); var nextDepth = depth + 1; // Loop through the innerKeys setting the intersects flag // to each result. Break out on false. do { var next = current[innerKey]; intersects = next !== undefined; if (intersects) { intersects = hasIntersection(next, path, nextDepth); } innerKey = iterateKeySet(key, note); } while (intersects && !note.done); // Since we recursed, we shall not pass any further! break; } // Its a simple key, just move forward with the testing. current = current[key]; intersects = current !== undefined; } return intersects; }; },{"139":139}],137:[function(require,module,exports){ // @flow /*:: import type { Key, KeySet, PathSet, Path, JsonGraph, JsonGraphNode, JsonMap } from "falcor-json-graph"; export type PathTree = { [key: string]: PathTree | null | void }; export type LengthTree = { [key: number]: PathTree | void }; export type IteratorNote = { done?: boolean }; type FalcorPathUtils = { iterateKeySet(keySet: KeySet, note: IteratorNote): Key; toTree(paths: PathSet[]): PathTree; pathsComplementFromTree(paths: PathSet[], tree: PathTree): PathSet[]; pathsComplementFromLengthTree(paths: PathSet[], tree: LengthTree): PathSet[]; toJsonKey(obj: JsonMap): string; isJsonKey(key: Key): boolean; maybeJsonKey(key: Key): JsonMap | void; hasIntersection(tree: PathTree, path: PathSet, depth: number): boolean; toPaths(lengths: LengthTree): PathSet[]; isIntegerKey(key: Key): boolean; maybeIntegerKey(key: Key): number | void; collapse(paths: PathSet[]): PathSet[]; followReference( cacheRoot: JsonGraph, ref: Path, maxRefFollow?: number ): { error: Error } | { error?: empty, node: ?JsonGraphNode, refPath: Path }; optimizePathSets( cache: JsonGraph, paths: PathSet[], maxRefFollow?: number ): { error: Error } | { error?: empty, paths: PathSet[] }; pathCount(path: PathSet): number; escape(key: string): string; unescape(key: string): string; materialize(pathSet: PathSet, value: JsonGraphNode): JsonGraphNode; }; */ module.exports = ({ iterateKeySet: require(139), toTree: require(149), pathsComplementFromTree: require(145), pathsComplementFromLengthTree: require(144), toJsonKey: require(140).toJsonKey, isJsonKey: require(140).isJsonKey, maybeJsonKey: require(140).maybeJsonKey, hasIntersection: require(136), toPaths: require(148), isIntegerKey: require(138).isIntegerKey, maybeIntegerKey: require(138).maybeIntegerKey, collapse: require(132), followReference: require(135), optimizePathSets: require(142), pathCount: require(143), escape: require(134), unescape: require(150), materialize: require(141) }/*: FalcorPathUtils*/); },{"132":132,"134":134,"135":135,"136":136,"138":138,"139":139,"140":140,"141":141,"142":142,"143":143,"144":144,"145":145,"148":148,"149":149,"150":150}],138:[function(require,module,exports){ "use strict"; var MAX_SAFE_INTEGER = 9007199254740991; // Number.MAX_SAFE_INTEGER in es6 var abs = Math.abs; var isSafeInteger = Number.isSafeInteger || function isSafeInteger(num) { return typeof num === "number" && num % 1 === 0 && abs(num) <= MAX_SAFE_INTEGER; } /** * Return number if argument is a number or can be cast to a number which * roundtrips to the same string, otherwise return undefined. */ function maybeIntegerKey(val) { if (typeof val === "string") { var num = Number(val); if(isSafeInteger(num) && String(num) === val) { return num; } } else if (isSafeInteger(val)) { return val; } } /** * Return true if argument is a number or can be cast to a number which * roundtrips to the same string. */ function isIntegerKey(val) { if (typeof val === "string") { var num = Number(val); return isSafeInteger(num) && String(num) === val; } return isSafeInteger(val); } module.exports.isIntegerKey = isIntegerKey; module.exports.maybeIntegerKey = maybeIntegerKey; },{}],139:[function(require,module,exports){ var isArray = Array.isArray; /** * Takes in a keySet and a note attempts to iterate over it. * If the value is a primitive, the key will be returned and the note will * be marked done * If the value is an object, then each value of the range will be returned * and when finished the note will be marked done. * If the value is an array, each value will be iterated over, if any of the * inner values are ranges, those will be iterated over. When fully done, * the note will be marked done. * * @param {Object|Array|String|Number} keySet - * @param {Object} note - The non filled note * @returns {String|Number|undefined} - The current iteration value. * If undefined, then the keySet is empty * @public */ module.exports = function iterateKeySet(keySet, note) { if (note.isArray === undefined) { /*#__NOINLINE__*/ initializeNote(keySet, note); } // Array iteration if (note.isArray) { var nextValue; // Cycle through the array and pluck out the next value. do { if (note.loaded && note.rangeOffset > note.to) { ++note.arrayOffset; note.loaded = false; } var idx = note.arrayOffset, length = keySet.length; if (idx >= length) { note.done = true; break; } var el = keySet[note.arrayOffset]; // Inner range iteration. if (el !== null && typeof el === 'object') { if (!note.loaded) { initializeRange(el, note); } // Empty to/from if (note.empty) { continue; } nextValue = note.rangeOffset++; } // Primitive iteration in array. else { ++note.arrayOffset; nextValue = el; } } while (nextValue === undefined); return nextValue; } // Range iteration else if (note.isObject) { if (!note.loaded) { initializeRange(keySet, note); } if (note.rangeOffset > note.to) { note.done = true; return undefined; } return note.rangeOffset++; } // Primitive value else { if (!note.loaded) { note.loaded = true; return keySet; } note.done = true; return undefined; } }; function initializeRange(key, memo) { var from = memo.from = key.from || 0; var to = memo.to = key.to || (typeof key.length === 'number' && memo.from + key.length - 1 || 0); memo.rangeOffset = memo.from; memo.loaded = true; if (from > to) { memo.empty = true; } } function initializeNote(key, note) { note.done = false; var isObject = note.isObject = !!(key && typeof key === 'object'); note.isArray = isObject && isArray(key); note.arrayOffset = 0; } },{}],140:[function(require,module,exports){ "use strict"; /** * Helper for getting a reproducible, key-sorted string representation of object. * Used to interpret an object as a falcor key. * @function * @param {Object} obj * @return stringified object with sorted keys. */ function toJsonKey(obj) { if (Object.prototype.toString.call(obj) === "[object Object]") { var key = JSON.stringify(obj, replacer); if (key[0] === "{") { return key; } } throw new TypeError("Only plain objects can be converted to JSON keys") } function replacer(key, value) { if (typeof value !== "object" || value === null || Array.isArray(value)) { return value; } return Object.keys(value) .sort() .reduce(function (acc, k) { acc[k] = value[k]; return acc; }, {}); } function maybeJsonKey(key) { if (typeof key !== 'string' || key[0] !== '{') { return; } var parsed; try { parsed = JSON.parse(key); } catch (e) { return; } if (JSON.stringify(parsed, replacer) !== key) { return; } return parsed; } function isJsonKey(key) { return typeof maybeJsonKey(key) !== "undefined"; } module.exports.toJsonKey = toJsonKey; module.exports.isJsonKey = isJsonKey; module.exports.maybeJsonKey = maybeJsonKey; },{}],141:[function(require,module,exports){ 'use strict'; var iterateKeySet = require(139); /** * Construct a jsonGraph from a pathSet and a value. * * @param {PathSet} pathSet - pathSet of paths at which to materialize value. * @param {JsonGraphNode} value - value to materialize at pathSet paths. * @returns {JsonGraphNode} - JsonGraph of value at pathSet paths. * @public */ module.exports = function materialize(pathSet, value) { return pathSet.reduceRight(function materializeInner(acc, keySet) { var branch = {}; if (typeof keySet !== 'object' || keySet === null) { branch[keySet] = acc; return branch; } var iteratorNote = {}; var key = iterateKeySet(keySet, iteratorNote); while (!iteratorNote.done) { branch[key] = acc; key = iterateKeySet(keySet, iteratorNote); } return branch; }, value); }; },{"139":139}],142:[function(require,module,exports){ var iterateKeySet = require(139); var cloneArray = require(147); var catAndSlice = require(146); var followReference = require(135); /** * The fastest possible optimize of paths. * * What it does: * - Any atom short-circuit / found value will be removed from the path. * - All paths will be exploded which means that collapse will need to be * ran afterwords. * - Any missing path will be optimized as much as possible. */ module.exports = function optimizePathSets(cache, paths, maxRefFollow) { if (typeof maxRefFollow === "undefined") { maxRefFollow = 5; } var optimized = []; for (var i = 0, len = paths.length; i < len; ++i) { var error = optimizePathSet(cache, cache, paths[i], 0, optimized, [], maxRefFollow); if (error) { return { error: error }; } } return { paths: optimized }; }; /** * optimizes one pathSet at a time. */ function optimizePathSet(cache, cacheRoot, pathSet, depth, out, optimizedPath, maxRefFollow) { // at missing, report optimized path. if (cache === undefined) { out[out.length] = catAndSlice(optimizedPath, pathSet, depth); return; } // all other sentinels are short circuited. // Or we found a primitive (which includes null) if (cache === null || (cache.$type && cache.$type !== "ref") || (typeof cache !== 'object')) { return; } // If the reference is the last item in the path then do not // continue to search it. if (cache.$type === "ref" && depth === pathSet.length) { return; } var keySet = pathSet[depth]; var isKeySet = typeof keySet === 'object' && keySet !== null; var nextDepth = depth + 1; var iteratorNote = false; var key = keySet; if (isKeySet) { iteratorNote = {}; key = iterateKeySet(keySet, iteratorNote); } var next, nextOptimized; do { next = cache[key]; var optimizedPathLength = optimizedPath.length; optimizedPath[optimizedPathLength] = key; if (next && next.$type === "ref" && nextDepth < pathSet.length) { var refResults = followReference(cacheRoot, next.value, maxRefFollow); if (refResults.error) { return refResults.error; } next = refResults.node; // must clone to avoid the mutation from above destroying the cache. nextOptimized = cloneArray(refResults.refPath); } else { nextOptimized = optimizedPath; } var error = optimizePathSet(next, cacheRoot, pathSet, nextDepth, out, nextOptimized, maxRefFollow); if (error) { return error; } optimizedPath.length = optimizedPathLength; if (iteratorNote && !iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (iteratorNote && !iteratorNote.done); } },{"135":135,"139":139,"146":146,"147":147}],143:[function(require,module,exports){ "use strict"; /** * Helper for getPathCount. Used to determine the size of a key or range. * @function * @param {Object} rangeOrKey * @return The size of the key or range passed in. */ function getRangeOrKeySize(rangeOrKey) { if (rangeOrKey == null) { return 1; } else if (Array.isArray(rangeOrKey)) { throw new Error("Unexpected Array found in keySet: " + JSON.stringify(rangeOrKey)); } else if (typeof rangeOrKey === "object") { return getRangeSize(rangeOrKey); } else { return 1; } } /** * Returns the size (number of items) in a Range, * @function * @param {Object} range The Range with both "from" and "to", or just "to" * @return The number of items in the range. */ function getRangeSize(range) { var to = range.to; var length = range.length; if (to != null) { if (isNaN(to) || parseInt(to, 10) !== to) { throw new Error("Invalid range, 'to' is not an integer: " + JSON.stringify(range)); } var from = range.from || 0; if (isNaN(from) || parseInt(from, 10) !== from) { throw new Error("Invalid range, 'from' is not an integer: " + JSON.stringify(range)); } if (from <= to) { return (to - from) + 1; } else { return 0; } } else if (length != null) { if (isNaN(length) || parseInt(length, 10) !== length) { throw new Error("Invalid range, 'length' is not an integer: " + JSON.stringify(range)); } else { return length; } } else { throw new Error("Invalid range, expected 'to' or 'length': " + JSON.stringify(range)); } } /** * Returns a count of the number of paths this pathset * represents. * * For example, ["foo", {"from":0, "to":10}, "bar"], * would represent 11 paths (0 to 10, inclusive), and * ["foo, ["baz", "boo"], "bar"] would represent 2 paths. * * @function * @param {Object[]} pathSet the path set. * * @return The number of paths this represents */ function getPathCount(pathSet) { if (pathSet.length === 0) { throw new Error("All paths must have length larger than zero."); } var numPaths = 1; for (var i = 0; i < pathSet.length; i++) { var segment = pathSet[i]; if (Array.isArray(segment)) { var numKeys = 0; for (var j = 0; j < segment.length; j++) { var keySet = segment[j]; numKeys += getRangeOrKeySize(keySet); } numPaths *= numKeys; } else { numPaths *= getRangeOrKeySize(segment); } } return numPaths; } module.exports = getPathCount; },{}],144:[function(require,module,exports){ var hasIntersection = require(136); /** * Compares the paths passed in with the tree. Any of the paths that are in * the tree will be stripped from the paths. * * **Does not mutate** the incoming paths object. * **Proper subset** only matching. * * @param {Array} paths - A list of paths (complex or simple) to strip the * intersection * @param {Object} tree - * @public */ module.exports = function pathsComplementFromLengthTree(paths, tree) { var out = []; var outLength = -1; for (var i = 0, len = paths.length; i < len; ++i) { // If this does not intersect then add it to the output. var path = paths[i]; if (!hasIntersection(tree[path.length], path, 0)) { out[++outLength] = path; } } return out; }; },{"136":136}],145:[function(require,module,exports){ var hasIntersection = require(136); /** * Compares the paths passed in with the tree. Any of the paths that are in * the tree will be stripped from the paths. * * **Does not mutate** the incoming paths object. * **Proper subset** only matching. * * @param {Array} paths - A list of paths (complex or simple) to strip the * intersection * @param {Object} tree - * @public */ module.exports = function pathsComplementFromTree(paths, tree) { var out = []; var outLength = -1; for (var i = 0, len = paths.length; i < len; ++i) { // If this does not intersect then add it to the output. if (!hasIntersection(tree, paths[i], 0)) { out[++outLength] = paths[i]; } } return out; }; },{"136":136}],146:[function(require,module,exports){ module.exports = function catAndSlice(a, b, slice) { var next = [], i, j, len; for (i = 0, len = a.length; i < len; ++i) { next[i] = a[i]; } for (j = slice || 0, len = b.length; j < len; ++j, ++i) { next[i] = b[j]; } return next; }; },{}],147:[function(require,module,exports){ function cloneArray(arr, index) { var a = []; var len = arr.length; for (var i = index || 0; i < len; i++) { a[i] = arr[i]; } return a; } module.exports = cloneArray; },{}],148:[function(require,module,exports){ var maybeIntegerKey = require(138).maybeIntegerKey; var isIntegerKey = require(138).isIntegerKey; var isArray = Array.isArray; var typeOfObject = "object"; var typeOfNumber = "number"; /* jshint forin: false */ module.exports = function toPaths(lengths) { var pathmap; var allPaths = []; for (var length in lengths) { var num = maybeIntegerKey(length); if (typeof num === typeOfNumber && isObject(pathmap = lengths[length])) { var paths = collapsePathMap(pathmap, 0, num).sets; var pathsIndex = -1; var pathsCount = paths.length; while (++pathsIndex < pathsCount) { allPaths.push(collapsePathSetIndexes(paths[pathsIndex])); } } } return allPaths; }; function isObject(value) { return value !== null && typeof value === typeOfObject; } function collapsePathMap(pathmap, depth, length) { var key; var code = getHashCode(String(depth)); var subs = Object.create(null); var codes = []; var codesIndex = -1; var codesCount = 0; var pathsets = []; var pathsetsCount = 0; var subPath, subCode, subKeys, subKeysIndex, subKeysCount, subSets, subSetsIndex, subSetsCount, pathset, pathsetIndex, pathsetCount, firstSubKey, pathsetClone; subKeys = []; subKeysIndex = -1; if (depth < length - 1) { subKeysCount = getKeys(pathmap, subKeys); while (++subKeysIndex < subKeysCount) { key = subKeys[subKeysIndex]; subPath = collapsePathMap(pathmap[key], depth + 1, length); subCode = subPath.code; if(subs[subCode]) { subPath = subs[subCode]; } else { codes[codesCount++] = subCode; subPath = subs[subCode] = { keys: [], sets: subPath.sets }; } code = getHashCode(code + key + subCode); var num = maybeIntegerKey(key); subPath.keys.push(typeof num === typeOfNumber ? num : key); } while(++codesIndex < codesCount) { key = codes[codesIndex]; subPath = subs[key]; subKeys = subPath.keys; subKeysCount = subKeys.length; if (subKeysCount > 0) { subSets = subPath.sets; subSetsIndex = -1; subSetsCount = subSets.length; firstSubKey = subKeys[0]; while (++subSetsIndex < subSetsCount) { pathset = subSets[subSetsIndex]; pathsetIndex = -1; pathsetCount = pathset.length; pathsetClone = new Array(pathsetCount + 1); pathsetClone[0] = subKeysCount > 1 && subKeys || firstSubKey; while (++pathsetIndex < pathsetCount) { pathsetClone[pathsetIndex + 1] = pathset[pathsetIndex]; } pathsets[pathsetsCount++] = pathsetClone; } } } } else { subKeysCount = getKeys(pathmap, subKeys); if (subKeysCount > 1) { pathsets[pathsetsCount++] = [subKeys]; } else { pathsets[pathsetsCount++] = subKeys; } while (++subKeysIndex < subKeysCount) { code = getHashCode(code + subKeys[subKeysIndex]); } } return { code: code, sets: pathsets }; } function collapsePathSetIndexes(pathset) { var keysetIndex = -1; var keysetCount = pathset.length; while (++keysetIndex < keysetCount) { var keyset = pathset[keysetIndex]; if (isArray(keyset)) { pathset[keysetIndex] = collapseIndex(keyset); } } return pathset; } /** * Collapse range indexers, e.g. when there is a continuous * range in an array, turn it into an object instead: * * [1,2,3,4,5,6] => {"from":1, "to":6} * * @private */ function collapseIndex(keyset) { // Do we need to dedupe an indexer keyset if they're duplicate consecutive integers? // var hash = {}; var keyIndex = -1; var keyCount = keyset.length - 1; var isSparseRange = keyCount > 0; while (++keyIndex <= keyCount) { var key = keyset[keyIndex]; if (!isIntegerKey(key) /* || hash[key] === true*/ ) { isSparseRange = false; break; } // hash[key] = true; // Cast number indexes to integers. keyset[keyIndex] = parseInt(key, 10); } if (isSparseRange === true) { keyset.sort(sortListAscending); var from = keyset[0]; var to = keyset[keyCount]; // If we re-introduce deduped integer indexers, change this comparson to "===". if (to - from <= keyCount) { return { from: from, to: to }; } } return keyset; } function sortListAscending(a, b) { return a - b; } /* jshint forin: false */ function getKeys(map, keys, sort) { var len = 0; for (var key in map) { keys[len++] = key; } return len; } function getHashCode(key) { var code = 5381; var index = -1; var count = key.length; while (++index < count) { code = (code << 5) + code + key.charCodeAt(index); } return String(code); } // backwards-compatibility (temporary) module.exports._isSafeNumber = isIntegerKey; },{"138":138}],149:[function(require,module,exports){ var iterateKeySet = require(139); /** * @param {Array} paths - * @returns {Object} - */ module.exports = function toTree(paths) { return paths.reduce(__reducer, {}); }; function __reducer(acc, path) { /*#__NOINLINE__*/ innerToTree(acc, path, 0); return acc; } function innerToTree(seed, path, depth) { var keySet = path[depth]; var iteratorNote = {}; var key; var nextDepth = depth + 1; key = iterateKeySet(keySet, iteratorNote); while (!iteratorNote.done) { var next = Object.prototype.hasOwnProperty.call(seed, key) && seed[key]; if (!next) { if (nextDepth === path.length) { seed[key] = null; } else if (key !== undefined) { next = seed[key] = {}; } } if (nextDepth < path.length) { innerToTree(next, path, nextDepth); } key = iterateKeySet(keySet, iteratorNote); } } },{"139":139}],150:[function(require,module,exports){ /** * Unescapes a string by removing the leading "_". This function is the inverse * of escape, which is used to encode untrusted input to ensure it * does not contain reserved JSON Graph keywords (ex. "$type"). */ module.exports = function unescape(str) { if (str.slice(0, 1) === "_") { return str.slice(1); } else { throw SyntaxError("Expected \"_\"."); } }; },{}],151:[function(require,module,exports){ arguments[4][132][0].apply(exports,arguments) },{"132":132,"164":164,"165":165}],152:[function(require,module,exports){ arguments[4][133][0].apply(exports,arguments) },{"133":133}],153:[function(require,module,exports){ var cloneArray = require(162); var $ref = require(163).$ref; var errors = require(152); /** * performs the simplified cache reference follow. This * differs from get as there is just following and reporting, * not much else. * * @param {Object} cacheRoot * @param {Array} ref */ module.exports = function followReference(cacheRoot, ref, maxRefFollow) { var current = cacheRoot; var refPath = ref; var depth = -1; var length = refPath.length; var key, next, type; var referenceCount = 0; while (++depth < length) { key = refPath[depth]; next = current[key]; type = next && next.$type; if (!next || type && type !== $ref) { current = next; break; } // Show stopper exception. This route is malformed. if (type && type === $ref && depth + 1 < length) { var err = new Error(errors.innerReferences); err.throwToNext = true; throw err; } // potentially follow reference if (depth + 1 === length) { if (type === $ref) { depth = -1; refPath = next.value; length = refPath.length; next = cacheRoot; referenceCount++; } if (referenceCount > maxRefFollow) { throw new Error(errors.circularReference); } } current = next; } return [current, cloneArray(refPath)]; }; },{"152":152,"162":162,"163":163}],154:[function(require,module,exports){ arguments[4][136][0].apply(exports,arguments) },{"136":136,"156":156}],155:[function(require,module,exports){ module.exports = { iterateKeySet: require(156), toTree: require(165), pathsComplementFromTree: require(160), pathsComplementFromLengthTree: require(159), hasIntersection: require(154), toPaths: require(164), collapse: require(151), optimizePathSets: require(157), pathCount: require(158) }; },{"151":151,"154":154,"156":156,"157":157,"158":158,"159":159,"160":160,"164":164,"165":165}],156:[function(require,module,exports){ var isArray = Array.isArray; /** * Takes in a keySet and a note attempts to iterate over it. * If the value is a primitive, the key will be returned and the note will * be marked done * If the value is an object, then each value of the range will be returned * and when finished the note will be marked done. * If the value is an array, each value will be iterated over, if any of the * inner values are ranges, those will be iterated over. When fully done, * the note will be marked done. * * @param {Object|Array|String|Number} keySet - * @param {Object} note - The non filled note * @returns {String|Number|undefined} - The current iteration value. * If undefined, then the keySet is empty * @public */ module.exports = function iterateKeySet(keySet, note) { if (note.isArray === undefined) { initializeNote(keySet, note); } // Array iteration if (note.isArray) { var nextValue; // Cycle through the array and pluck out the next value. do { if (note.loaded && note.rangeOffset > note.to) { ++note.arrayOffset; note.loaded = false; } var idx = note.arrayOffset, length = keySet.length; if (idx >= length) { note.done = true; break; } var el = keySet[note.arrayOffset]; var type = typeof el; // Inner range iteration. if (type === 'object') { if (!note.loaded) { initializeRange(el, note); } // Empty to/from if (note.empty) { continue; } nextValue = note.rangeOffset++; } // Primitive iteration in array. else { ++note.arrayOffset; nextValue = el; } } while (nextValue === undefined); return nextValue; } // Range iteration else if (note.isObject) { if (!note.loaded) { initializeRange(keySet, note); } if (note.rangeOffset > note.to) { note.done = true; return undefined; } return note.rangeOffset++; } // Primitive value else { note.done = true; return keySet; } }; function initializeRange(key, memo) { var from = memo.from = key.from || 0; var to = memo.to = key.to || (typeof key.length === 'number' && memo.from + key.length - 1 || 0); memo.rangeOffset = memo.from; memo.loaded = true; if (from > to) { memo.empty = true; } } function initializeNote(key, note) { note.done = false; var isObject = note.isObject = !!(key && typeof key === 'object'); note.isArray = isObject && isArray(key); note.arrayOffset = 0; } },{}],157:[function(require,module,exports){ var iterateKeySet = require(156); var cloneArray = require(162); var catAndSlice = require(161); var $types = require(163); var $ref = $types.$ref; var followReference = require(153); /** * The fastest possible optimize of paths. * * What it does: * - Any atom short-circuit / found value will be removed from the path. * - All paths will be exploded which means that collapse will need to be * ran afterwords. * - Any missing path will be optimized as much as possible. */ module.exports = function optimizePathSets(cache, paths, maxRefFollow) { var optimized = []; paths.forEach(function(p) { optimizePathSet(cache, cache, p, 0, optimized, [], maxRefFollow); }); return optimized; }; /** * optimizes one pathSet at a time. */ function optimizePathSet(cache, cacheRoot, pathSet, depth, out, optimizedPath, maxRefFollow) { // at missing, report optimized path. if (cache === undefined) { out[out.length] = catAndSlice(optimizedPath, pathSet, depth); return; } // all other sentinels are short circuited. // Or we found a primitive (which includes null) if (cache === null || (cache.$type && cache.$type !== $ref) || (typeof cache !== 'object')) { return; } // If the reference is the last item in the path then do not // continue to search it. if (cache.$type === $ref && depth === pathSet.length) { return; } var keySet = pathSet[depth]; var isKeySet = typeof keySet === 'object'; var nextDepth = depth + 1; var iteratorNote = false; var key = keySet; if (isKeySet) { iteratorNote = {}; key = iterateKeySet(keySet, iteratorNote); } var next, nextOptimized; do { next = cache[key]; var optimizedPathLength = optimizedPath.length; if (key !== null) { optimizedPath[optimizedPathLength] = key; } if (next && next.$type === $ref && nextDepth < pathSet.length) { var refResults = followReference(cacheRoot, next.value, maxRefFollow); next = refResults[0]; // must clone to avoid the mutation from above destroying the cache. nextOptimized = cloneArray(refResults[1]); } else { nextOptimized = optimizedPath; } optimizePathSet(next, cacheRoot, pathSet, nextDepth, out, nextOptimized, maxRefFollow); optimizedPath.length = optimizedPathLength; if (iteratorNote && !iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (iteratorNote && !iteratorNote.done); } },{"153":153,"156":156,"161":161,"162":162,"163":163}],158:[function(require,module,exports){ arguments[4][143][0].apply(exports,arguments) },{"143":143}],159:[function(require,module,exports){ arguments[4][144][0].apply(exports,arguments) },{"144":144,"154":154}],160:[function(require,module,exports){ arguments[4][145][0].apply(exports,arguments) },{"145":145,"154":154}],161:[function(require,module,exports){ arguments[4][146][0].apply(exports,arguments) },{"146":146}],162:[function(require,module,exports){ arguments[4][147][0].apply(exports,arguments) },{"147":147}],163:[function(require,module,exports){ module.exports = { $ref: 'ref', $atom: 'atom', $error: 'error' }; },{}],164:[function(require,module,exports){ var isArray = Array.isArray; var typeOfObject = "object"; var typeOfString = "string"; var typeOfNumber = "number"; var MAX_SAFE_INTEGER = 9007199254740991; // Number.MAX_SAFE_INTEGER in es6 var MAX_SAFE_INTEGER_DIGITS = 16; // String(Number.MAX_SAFE_INTEGER).length var MIN_SAFE_INTEGER_DIGITS = 17; // String(Number.MIN_SAFE_INTEGER).length (including sign) var abs = Math.abs; var safeNumberRegEx = /^(0|(\-?[1-9][0-9]*))$/; /* jshint forin: false */ module.exports = function toPaths(lengths) { var pathmap; var allPaths = []; var allPathsLength = 0; for (var length in lengths) { if (isSafeNumber(length) && isObject(pathmap = lengths[length])) { var paths = collapsePathMap(pathmap, 0, parseInt(length, 10)).sets; var pathsIndex = -1; var pathsCount = paths.length; while (++pathsIndex < pathsCount) { allPaths[allPathsLength++] = collapsePathSetIndexes(paths[pathsIndex]); } } } return allPaths; }; function isObject(value) { return value !== null && typeof value === typeOfObject; } function collapsePathMap(pathmap, depth, length) { var key; var code = getHashCode(String(depth)); var subs = Object.create(null); var codes = []; var codesIndex = -1; var codesCount = 0; var pathsets = []; var pathsetsCount = 0; var subPath, subCode, subKeys, subKeysIndex, subKeysCount, subSets, subSetsIndex, subSetsCount, pathset, pathsetIndex, pathsetCount, firstSubKey, pathsetClone; subKeys = []; subKeysIndex = -1; if (depth < length - 1) { subKeysCount = getSortedKeys(pathmap, subKeys); while (++subKeysIndex < subKeysCount) { key = subKeys[subKeysIndex]; subPath = collapsePathMap(pathmap[key], depth + 1, length); subCode = subPath.code; if(subs[subCode]) { subPath = subs[subCode]; } else { codes[codesCount++] = subCode; subPath = subs[subCode] = { keys: [], sets: subPath.sets }; } code = getHashCode(code + key + subCode); isSafeNumber(key) && subPath.keys.push(parseInt(key, 10)) || subPath.keys.push(key); } while(++codesIndex < codesCount) { key = codes[codesIndex]; subPath = subs[key]; subKeys = subPath.keys; subKeysCount = subKeys.length; if (subKeysCount > 0) { subSets = subPath.sets; subSetsIndex = -1; subSetsCount = subSets.length; firstSubKey = subKeys[0]; while (++subSetsIndex < subSetsCount) { pathset = subSets[subSetsIndex]; pathsetIndex = -1; pathsetCount = pathset.length; pathsetClone = new Array(pathsetCount + 1); pathsetClone[0] = subKeysCount > 1 && subKeys || firstSubKey; while (++pathsetIndex < pathsetCount) { pathsetClone[pathsetIndex + 1] = pathset[pathsetIndex]; } pathsets[pathsetsCount++] = pathsetClone; } } } } else { subKeysCount = getSortedKeys(pathmap, subKeys); if (subKeysCount > 1) { pathsets[pathsetsCount++] = [subKeys]; } else { pathsets[pathsetsCount++] = subKeys; } while (++subKeysIndex < subKeysCount) { code = getHashCode(code + subKeys[subKeysIndex]); } } return { code: code, sets: pathsets }; } function collapsePathSetIndexes(pathset) { var keysetIndex = -1; var keysetCount = pathset.length; while (++keysetIndex < keysetCount) { var keyset = pathset[keysetIndex]; if (isArray(keyset)) { pathset[keysetIndex] = collapseIndex(keyset); } } return pathset; } /** * Collapse range indexers, e.g. when there is a continuous * range in an array, turn it into an object instead: * * [1,2,3,4,5,6] => {"from":1, "to":6} * * @private */ function collapseIndex(keyset) { // Do we need to dedupe an indexer keyset if they're duplicate consecutive integers? // var hash = {}; var keyIndex = -1; var keyCount = keyset.length - 1; var isSparseRange = keyCount > 0; while (++keyIndex <= keyCount) { var key = keyset[keyIndex]; if (!isSafeNumber(key) /* || hash[key] === true*/ ) { isSparseRange = false; break; } // hash[key] = true; // Cast number indexes to integers. keyset[keyIndex] = parseInt(key, 10); } if (isSparseRange === true) { keyset.sort(sortListAscending); var from = keyset[0]; var to = keyset[keyCount]; // If we re-introduce deduped integer indexers, change this comparson to "===". if (to - from <= keyCount) { return { from: from, to: to }; } } return keyset; } function sortListAscending(a, b) { return a - b; } /* jshint forin: false */ function getSortedKeys(map, keys, sort) { var len = 0; for (var key in map) { keys[len++] = key; } if (len > 1) { keys.sort(sort); } return len; } function getHashCode(key) { var code = 5381; var index = -1; var count = key.length; while (++index < count) { code = (code << 5) + code + key.charCodeAt(index); } return String(code); } /** * Return true if argument is a number or can be cast to a number which * roundtrips to the same string. * @private */ function isSafeNumber(val) { var num = val; var type = typeof val; if (type === typeOfString) { var length = val.length; // Number.MIN_SAFE_INTEGER is 17 digits including the sign. // Anything longer cannot be safe. if (length === 0 || length > MIN_SAFE_INTEGER_DIGITS) { return false; } if (!safeNumberRegEx.test(val)) { return false; } // Number.MAX_SAFE_INTEGER is 16 digits. // Anything shorter must be safe. if (length < MAX_SAFE_INTEGER_DIGITS) { return true; } num = +val; } else if (type !== typeOfNumber) { return false; } // Number.isSafeInteger(num) in es6. return num % 1 === 0 && abs(num) <= MAX_SAFE_INTEGER; } // export for testing module.exports._isSafeNumber = isSafeNumber; },{}],165:[function(require,module,exports){ var iterateKeySet = require(156); var isArray = Array.isArray; /** * @param {Array} paths - * @returns {Object} - */ module.exports = function toTree(paths) { return paths.reduce(function(acc, path) { innerToTree(acc, path, 0); return acc; }, {}); }; function innerToTree(seed, path, depth) { var keySet = path[depth]; var iteratorNote = {}; var key; var nextDepth = depth + 1; key = iterateKeySet(keySet, iteratorNote); do { var next = seed[key]; if (!next) { if (nextDepth === path.length) { seed[key] = null; } else { next = seed[key] = {}; } } if (nextDepth < path.length) { innerToTree(next, path, nextDepth); } if (!iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (!iteratorNote.done); } },{"156":156}],166:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var InnerSubscriber = (function (_super) { __extends(InnerSubscriber, _super); function InnerSubscriber(parent, outerValue, outerIndex) { _super.call(this); this.parent = parent; this.outerValue = outerValue; this.outerIndex = outerIndex; this.index = 0; } InnerSubscriber.prototype._next = function (value) { this.parent.notifyNext(this.outerValue, value, this.outerIndex, this.index++, this); }; InnerSubscriber.prototype._error = function (error) { this.parent.notifyError(error, this); this.unsubscribe(); }; InnerSubscriber.prototype._complete = function () { this.parent.notifyComplete(this); this.unsubscribe(); }; return InnerSubscriber; }(Subscriber_1.Subscriber)); exports.InnerSubscriber = InnerSubscriber; },{"172":172}],167:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); /** * Represents a push-based event or value that an {@link Observable} can emit. * This class is particularly useful for operators that manage notifications, * like {@link materialize}, {@link dematerialize}, {@link observeOn}, and * others. Besides wrapping the actual delivered value, it also annotates it * with metadata of, for instance, what type of push message it is (`next`, * `error`, or `complete`). * * @see {@link materialize} * @see {@link dematerialize} * @see {@link observeOn} * * @class Notification */ var Notification = (function () { function Notification(kind, value, error) { this.kind = kind; this.value = value; this.error = error; this.hasValue = kind === 'N'; } /** * Delivers to the given `observer` the value wrapped by this Notification. * @param {Observer} observer * @return */ Notification.prototype.observe = function (observer) { switch (this.kind) { case 'N': return observer.next && observer.next(this.value); case 'E': return observer.error && observer.error(this.error); case 'C': return observer.complete && observer.complete(); } }; /** * Given some {@link Observer} callbacks, deliver the value represented by the * current Notification to the correctly corresponding callback. * @param {function(value: T): void} next An Observer `next` callback. * @param {function(err: any): void} [error] An Observer `error` callback. * @param {function(): void} [complete] An Observer `complete` callback. * @return {any} */ Notification.prototype.do = function (next, error, complete) { var kind = this.kind; switch (kind) { case 'N': return next && next(this.value); case 'E': return error && error(this.error); case 'C': return complete && complete(); } }; /** * Takes an Observer or its individual callback functions, and calls `observe` * or `do` methods accordingly. * @param {Observer|function(value: T): void} nextOrObserver An Observer or * the `next` callback. * @param {function(err: any): void} [error] An Observer `error` callback. * @param {function(): void} [complete] An Observer `complete` callback. * @return {any} */ Notification.prototype.accept = function (nextOrObserver, error, complete) { if (nextOrObserver && typeof nextOrObserver.next === 'function') { return this.observe(nextOrObserver); } else { return this.do(nextOrObserver, error, complete); } }; /** * Returns a simple Observable that just delivers the notification represented * by this Notification instance. * @return {any} */ Notification.prototype.toObservable = function () { var kind = this.kind; switch (kind) { case 'N': return Observable_1.Observable.of(this.value); case 'E': return Observable_1.Observable.throw(this.error); case 'C': return Observable_1.Observable.empty(); } throw new Error('unexpected notification kind value'); }; /** * A shortcut to create a Notification instance of the type `next` from a * given value. * @param {T} value The `next` value. * @return {Notification} The "next" Notification representing the * argument. */ Notification.createNext = function (value) { if (typeof value !== 'undefined') { return new Notification('N', value); } return this.undefinedValueNotification; }; /** * A shortcut to create a Notification instance of the type `error` from a * given error. * @param {any} [err] The `error` error. * @return {Notification} The "error" Notification representing the * argument. */ Notification.createError = function (err) { return new Notification('E', undefined, err); }; /** * A shortcut to create a Notification instance of the type `complete`. * @return {Notification} The valueless "complete" Notification. */ Notification.createComplete = function () { return this.completeNotification; }; Notification.completeNotification = new Notification('C'); Notification.undefinedValueNotification = new Notification('N', undefined); return Notification; }()); exports.Notification = Notification; },{"168":168}],168:[function(require,module,exports){ "use strict"; var root_1 = require(233); var toSubscriber_1 = require(235); var observable_1 = require(224); /** * A representation of any set of values over any amount of time. This the most basic building block * of RxJS. * * @class Observable */ var Observable = (function () { /** * @constructor * @param {Function} subscribe the function that is called when the Observable is * initially subscribed to. This function is given a Subscriber, to which new values * can be `next`ed, or an `error` method can be called to raise an error, or * `complete` can be called to notify of a successful completion. */ function Observable(subscribe) { this._isScalar = false; if (subscribe) { this._subscribe = subscribe; } } /** * Creates a new Observable, with this Observable as the source, and the passed * operator defined as the new observable's operator. * @method lift * @param {Operator} operator the operator defining the operation to take on the observable * @return {Observable} a new observable with the Operator applied */ Observable.prototype.lift = function (operator) { var observable = new Observable(); observable.source = this; observable.operator = operator; return observable; }; Observable.prototype.subscribe = function (observerOrNext, error, complete) { var operator = this.operator; var sink = toSubscriber_1.toSubscriber(observerOrNext, error, complete); if (operator) { operator.call(sink, this.source); } else { sink.add(this._trySubscribe(sink)); } if (sink.syncErrorThrowable) { sink.syncErrorThrowable = false; if (sink.syncErrorThrown) { throw sink.syncErrorValue; } } return sink; }; Observable.prototype._trySubscribe = function (sink) { try { return this._subscribe(sink); } catch (err) { sink.syncErrorThrown = true; sink.syncErrorValue = err; sink.error(err); } }; /** * @method forEach * @param {Function} next a handler for each value emitted by the observable * @param {PromiseConstructor} [PromiseCtor] a constructor function used to instantiate the Promise * @return {Promise} a promise that either resolves on observable completion or * rejects with the handled error */ Observable.prototype.forEach = function (next, PromiseCtor) { var _this = this; if (!PromiseCtor) { if (root_1.root.Rx && root_1.root.Rx.config && root_1.root.Rx.config.Promise) { PromiseCtor = root_1.root.Rx.config.Promise; } else if (root_1.root.Promise) { PromiseCtor = root_1.root.Promise; } } if (!PromiseCtor) { throw new Error('no Promise impl found'); } return new PromiseCtor(function (resolve, reject) { var subscription = _this.subscribe(function (value) { if (subscription) { // if there is a subscription, then we can surmise // the next handling is asynchronous. Any errors thrown // need to be rejected explicitly and unsubscribe must be // called manually try { next(value); } catch (err) { reject(err); subscription.unsubscribe(); } } else { // if there is NO subscription, then we're getting a nexted // value synchronously during subscription. We can just call it. // If it errors, Observable's `subscribe` will ensure the // unsubscription logic is called, then synchronously rethrow the error. // After that, Promise will trap the error and send it // down the rejection path. next(value); } }, reject, resolve); }); }; Observable.prototype._subscribe = function (subscriber) { return this.source.subscribe(subscriber); }; /** * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable * @method Symbol.observable * @return {Observable} this instance of the observable */ Observable.prototype[observable_1.$$observable] = function () { return this; }; // HACK: Since TypeScript inherits static properties too, we have to // fight against TypeScript here so Subject can have a different static create signature /** * Creates a new cold Observable by calling the Observable constructor * @static true * @owner Observable * @method create * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor * @return {Observable} a new cold observable */ Observable.create = function (subscribe) { return new Observable(subscribe); }; return Observable; }()); exports.Observable = Observable; },{"224":224,"233":233,"235":235}],169:[function(require,module,exports){ "use strict"; exports.empty = { closed: true, next: function (value) { }, error: function (err) { throw err; }, complete: function () { } }; },{}],170:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var OuterSubscriber = (function (_super) { __extends(OuterSubscriber, _super); function OuterSubscriber() { _super.apply(this, arguments); } OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { this.destination.next(innerValue); }; OuterSubscriber.prototype.notifyError = function (error, innerSub) { this.destination.error(error); }; OuterSubscriber.prototype.notifyComplete = function (innerSub) { this.destination.complete(); }; return OuterSubscriber; }(Subscriber_1.Subscriber)); exports.OuterSubscriber = OuterSubscriber; },{"172":172}],171:[function(require,module,exports){ "use strict"; /** * An execution context and a data structure to order tasks and schedule their * execution. Provides a notion of (potentially virtual) time, through the * `now()` getter method. * * Each unit of work in a Scheduler is called an {@link Action}. * * ```ts * class Scheduler { * now(): number; * schedule(work, delay?, state?): Subscription; * } * ``` * * @class Scheduler */ var Scheduler = (function () { function Scheduler(SchedulerAction, now) { if (now === void 0) { now = Scheduler.now; } this.SchedulerAction = SchedulerAction; this.now = now; } /** * Schedules a function, `work`, for execution. May happen at some point in * the future, according to the `delay` parameter, if specified. May be passed * some context object, `state`, which will be passed to the `work` function. * * The given arguments will be processed an stored as an Action object in a * queue of actions. * * @param {function(state: ?T): ?Subscription} work A function representing a * task, or some unit of work to be executed by the Scheduler. * @param {number} [delay] Time to wait before executing the work, where the * time unit is implicit and defined by the Scheduler itself. * @param {T} [state] Some contextual data that the `work` function uses when * called by the Scheduler. * @return {Subscription} A subscription in order to be able to unsubscribe * the scheduled work. */ Scheduler.prototype.schedule = function (work, delay, state) { if (delay === void 0) { delay = 0; } return new this.SchedulerAction(this, work).schedule(state, delay); }; Scheduler.now = Date.now ? Date.now : function () { return +new Date(); }; return Scheduler; }()); exports.Scheduler = Scheduler; },{}],172:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var isFunction_1 = require(229); var Subscription_1 = require(173); var Observer_1 = require(169); var rxSubscriber_1 = require(225); /** * Implements the {@link Observer} interface and extends the * {@link Subscription} class. While the {@link Observer} is the public API for * consuming the values of an {@link Observable}, all Observers get converted to * a Subscriber, in order to provide Subscription-like capabilities such as * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for * implementing operators, but it is rarely used as a public API. * * @class Subscriber */ var Subscriber = (function (_super) { __extends(Subscriber, _super); /** * @param {Observer|function(value: T): void} [destinationOrNext] A partially * defined Observer or a `next` callback function. * @param {function(e: ?any): void} [error] The `error` callback of an * Observer. * @param {function(): void} [complete] The `complete` callback of an * Observer. */ function Subscriber(destinationOrNext, error, complete) { _super.call(this); this.syncErrorValue = null; this.syncErrorThrown = false; this.syncErrorThrowable = false; this.isStopped = false; switch (arguments.length) { case 0: this.destination = Observer_1.empty; break; case 1: if (!destinationOrNext) { this.destination = Observer_1.empty; break; } if (typeof destinationOrNext === 'object') { if (destinationOrNext instanceof Subscriber) { this.destination = destinationOrNext; this.destination.add(this); } else { this.syncErrorThrowable = true; this.destination = new SafeSubscriber(this, destinationOrNext); } break; } default: this.syncErrorThrowable = true; this.destination = new SafeSubscriber(this, destinationOrNext, error, complete); break; } } Subscriber.prototype[rxSubscriber_1.$$rxSubscriber] = function () { return this; }; /** * A static factory for a Subscriber, given a (potentially partial) definition * of an Observer. * @param {function(x: ?T): void} [next] The `next` callback of an Observer. * @param {function(e: ?any): void} [error] The `error` callback of an * Observer. * @param {function(): void} [complete] The `complete` callback of an * Observer. * @return {Subscriber} A Subscriber wrapping the (partially defined) * Observer represented by the given arguments. */ Subscriber.create = function (next, error, complete) { var subscriber = new Subscriber(next, error, complete); subscriber.syncErrorThrowable = false; return subscriber; }; /** * The {@link Observer} callback to receive notifications of type `next` from * the Observable, with a value. The Observable may call this method 0 or more * times. * @param {T} [value] The `next` value. * @return {void} */ Subscriber.prototype.next = function (value) { if (!this.isStopped) { this._next(value); } }; /** * The {@link Observer} callback to receive notifications of type `error` from * the Observable, with an attached {@link Error}. Notifies the Observer that * the Observable has experienced an error condition. * @param {any} [err] The `error` exception. * @return {void} */ Subscriber.prototype.error = function (err) { if (!this.isStopped) { this.isStopped = true; this._error(err); } }; /** * The {@link Observer} callback to receive a valueless notification of type * `complete` from the Observable. Notifies the Observer that the Observable * has finished sending push-based notifications. * @return {void} */ Subscriber.prototype.complete = function () { if (!this.isStopped) { this.isStopped = true; this._complete(); } }; Subscriber.prototype.unsubscribe = function () { if (this.closed) { return; } this.isStopped = true; _super.prototype.unsubscribe.call(this); }; Subscriber.prototype._next = function (value) { this.destination.next(value); }; Subscriber.prototype._error = function (err) { this.destination.error(err); this.unsubscribe(); }; Subscriber.prototype._complete = function () { this.destination.complete(); this.unsubscribe(); }; Subscriber.prototype._unsubscribeAndRecycle = function () { var _a = this, _parent = _a._parent, _parents = _a._parents; this._parent = null; this._parents = null; this.unsubscribe(); this.closed = false; this.isStopped = false; this._parent = _parent; this._parents = _parents; return this; }; return Subscriber; }(Subscription_1.Subscription)); exports.Subscriber = Subscriber; /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var SafeSubscriber = (function (_super) { __extends(SafeSubscriber, _super); function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) { _super.call(this); this._parentSubscriber = _parentSubscriber; var next; var context = this; if (isFunction_1.isFunction(observerOrNext)) { next = observerOrNext; } else if (observerOrNext) { context = observerOrNext; next = observerOrNext.next; error = observerOrNext.error; complete = observerOrNext.complete; if (isFunction_1.isFunction(context.unsubscribe)) { this.add(context.unsubscribe.bind(context)); } context.unsubscribe = this.unsubscribe.bind(this); } this._context = context; this._next = next; this._error = error; this._complete = complete; } SafeSubscriber.prototype.next = function (value) { if (!this.isStopped && this._next) { var _parentSubscriber = this._parentSubscriber; if (!_parentSubscriber.syncErrorThrowable) { this.__tryOrUnsub(this._next, value); } else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) { this.unsubscribe(); } } }; SafeSubscriber.prototype.error = function (err) { if (!this.isStopped) { var _parentSubscriber = this._parentSubscriber; if (this._error) { if (!_parentSubscriber.syncErrorThrowable) { this.__tryOrUnsub(this._error, err); this.unsubscribe(); } else { this.__tryOrSetError(_parentSubscriber, this._error, err); this.unsubscribe(); } } else if (!_parentSubscriber.syncErrorThrowable) { this.unsubscribe(); throw err; } else { _parentSubscriber.syncErrorValue = err; _parentSubscriber.syncErrorThrown = true; this.unsubscribe(); } } }; SafeSubscriber.prototype.complete = function () { if (!this.isStopped) { var _parentSubscriber = this._parentSubscriber; if (this._complete) { if (!_parentSubscriber.syncErrorThrowable) { this.__tryOrUnsub(this._complete); this.unsubscribe(); } else { this.__tryOrSetError(_parentSubscriber, this._complete); this.unsubscribe(); } } else { this.unsubscribe(); } } }; SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) { try { fn.call(this._context, value); } catch (err) { this.unsubscribe(); throw err; } }; SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) { try { fn.call(this._context, value); } catch (err) { parent.syncErrorValue = err; parent.syncErrorThrown = true; return true; } return false; }; SafeSubscriber.prototype._unsubscribe = function () { var _parentSubscriber = this._parentSubscriber; this._context = null; this._parentSubscriber = null; _parentSubscriber.unsubscribe(); }; return SafeSubscriber; }(Subscriber)); },{"169":169,"173":173,"225":225,"229":229}],173:[function(require,module,exports){ "use strict"; var isArray_1 = require(228); var isObject_1 = require(230); var isFunction_1 = require(229); var tryCatch_1 = require(236); var errorObject_1 = require(227); var UnsubscriptionError_1 = require(226); /** * Represents a disposable resource, such as the execution of an Observable. A * Subscription has one important method, `unsubscribe`, that takes no argument * and just disposes the resource held by the subscription. * * Additionally, subscriptions may be grouped together through the `add()` * method, which will attach a child Subscription to the current Subscription. * When a Subscription is unsubscribed, all its children (and its grandchildren) * will be unsubscribed as well. * * @class Subscription */ var Subscription = (function () { /** * @param {function(): void} [unsubscribe] A function describing how to * perform the disposal of resources when the `unsubscribe` method is called. */ function Subscription(unsubscribe) { /** * A flag to indicate whether this Subscription has already been unsubscribed. * @type {boolean} */ this.closed = false; this._parent = null; this._parents = null; this._subscriptions = null; if (unsubscribe) { this._unsubscribe = unsubscribe; } } /** * Disposes the resources held by the subscription. May, for instance, cancel * an ongoing Observable execution or cancel any other type of work that * started when the Subscription was created. * @return {void} */ Subscription.prototype.unsubscribe = function () { var hasErrors = false; var errors; if (this.closed) { return; } var _a = this, _parent = _a._parent, _parents = _a._parents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; this.closed = true; this._parent = null; this._parents = null; // null out _subscriptions first so any child subscriptions that attempt // to remove themselves from this subscription will noop this._subscriptions = null; var index = -1; var len = _parents ? _parents.length : 0; // if this._parent is null, then so is this._parents, and we // don't have to remove ourselves from any parent subscriptions. while (_parent) { _parent.remove(this); // if this._parents is null or index >= len, // then _parent is set to null, and the loop exits _parent = ++index < len && _parents[index] || null; } if (isFunction_1.isFunction(_unsubscribe)) { var trial = tryCatch_1.tryCatch(_unsubscribe).call(this); if (trial === errorObject_1.errorObject) { hasErrors = true; errors = errors || (errorObject_1.errorObject.e instanceof UnsubscriptionError_1.UnsubscriptionError ? flattenUnsubscriptionErrors(errorObject_1.errorObject.e.errors) : [errorObject_1.errorObject.e]); } } if (isArray_1.isArray(_subscriptions)) { index = -1; len = _subscriptions.length; while (++index < len) { var sub = _subscriptions[index]; if (isObject_1.isObject(sub)) { var trial = tryCatch_1.tryCatch(sub.unsubscribe).call(sub); if (trial === errorObject_1.errorObject) { hasErrors = true; errors = errors || []; var err = errorObject_1.errorObject.e; if (err instanceof UnsubscriptionError_1.UnsubscriptionError) { errors = errors.concat(flattenUnsubscriptionErrors(err.errors)); } else { errors.push(err); } } } } } if (hasErrors) { throw new UnsubscriptionError_1.UnsubscriptionError(errors); } }; /** * Adds a tear down to be called during the unsubscribe() of this * Subscription. * * If the tear down being added is a subscription that is already * unsubscribed, is the same reference `add` is being called on, or is * `Subscription.EMPTY`, it will not be added. * * If this subscription is already in an `closed` state, the passed * tear down logic will be executed immediately. * * @param {TeardownLogic} teardown The additional logic to execute on * teardown. * @return {Subscription} Returns the Subscription used or created to be * added to the inner subscriptions list. This Subscription can be used with * `remove()` to remove the passed teardown logic from the inner subscriptions * list. */ Subscription.prototype.add = function (teardown) { if (!teardown || (teardown === Subscription.EMPTY)) { return Subscription.EMPTY; } if (teardown === this) { return this; } var subscription = teardown; switch (typeof teardown) { case 'function': subscription = new Subscription(teardown); case 'object': if (subscription.closed || typeof subscription.unsubscribe !== 'function') { return subscription; } else if (this.closed) { subscription.unsubscribe(); return subscription; } else if (typeof subscription._addParent !== 'function' /* quack quack */) { var tmp = subscription; subscription = new Subscription(); subscription._subscriptions = [tmp]; } break; default: throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); } var subscriptions = this._subscriptions || (this._subscriptions = []); subscriptions.push(subscription); subscription._addParent(this); return subscription; }; /** * Removes a Subscription from the internal list of subscriptions that will * unsubscribe during the unsubscribe process of this Subscription. * @param {Subscription} subscription The subscription to remove. * @return {void} */ Subscription.prototype.remove = function (subscription) { var subscriptions = this._subscriptions; if (subscriptions) { var subscriptionIndex = subscriptions.indexOf(subscription); if (subscriptionIndex !== -1) { subscriptions.splice(subscriptionIndex, 1); } } }; Subscription.prototype._addParent = function (parent) { var _a = this, _parent = _a._parent, _parents = _a._parents; if (!_parent || _parent === parent) { // If we don't have a parent, or the new parent is the same as the // current parent, then set this._parent to the new parent. this._parent = parent; } else if (!_parents) { // If there's already one parent, but not multiple, allocate an Array to // store the rest of the parent Subscriptions. this._parents = [parent]; } else if (_parents.indexOf(parent) === -1) { // Only add the new parent to the _parents list if it's not already there. _parents.push(parent); } }; Subscription.EMPTY = (function (empty) { empty.closed = true; return empty; }(new Subscription())); return Subscription; }()); exports.Subscription = Subscription; function flattenUnsubscriptionErrors(errors) { return errors.reduce(function (errs, err) { return errs.concat((err instanceof UnsubscriptionError_1.UnsubscriptionError) ? err.errors : err); }, []); } },{"226":226,"227":227,"228":228,"229":229,"230":230,"236":236}],174:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var defer_1 = require(199); Observable_1.Observable.defer = defer_1.defer; },{"168":168,"199":199}],175:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var empty_1 = require(200); Observable_1.Observable.empty = empty_1.empty; },{"168":168,"200":200}],176:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var from_1 = require(201); Observable_1.Observable.from = from_1.from; },{"168":168,"201":201}],177:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var of_1 = require(202); Observable_1.Observable.of = of_1.of; },{"168":168,"202":202}],178:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var throw_1 = require(203); Observable_1.Observable.throw = throw_1._throw; },{"168":168,"203":203}],179:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var catch_1 = require(204); Observable_1.Observable.prototype.catch = catch_1._catch; Observable_1.Observable.prototype._catch = catch_1._catch; },{"168":168,"204":204}],180:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var concat_1 = require(205); Observable_1.Observable.prototype.concat = concat_1.concat; },{"168":168,"205":205}],181:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var defaultIfEmpty_1 = require(206); Observable_1.Observable.prototype.defaultIfEmpty = defaultIfEmpty_1.defaultIfEmpty; },{"168":168,"206":206}],182:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var do_1 = require(207); Observable_1.Observable.prototype.do = do_1._do; Observable_1.Observable.prototype._do = do_1._do; },{"168":168,"207":207}],183:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var expand_1 = require(208); Observable_1.Observable.prototype.expand = expand_1.expand; },{"168":168,"208":208}],184:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var filter_1 = require(209); Observable_1.Observable.prototype.filter = filter_1.filter; },{"168":168,"209":209}],185:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var map_1 = require(210); Observable_1.Observable.prototype.map = map_1.map; },{"168":168,"210":210}],186:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var materialize_1 = require(211); Observable_1.Observable.prototype.materialize = materialize_1.materialize; },{"168":168,"211":211}],187:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var mergeMap_1 = require(213); Observable_1.Observable.prototype.mergeMap = mergeMap_1.mergeMap; Observable_1.Observable.prototype.flatMap = mergeMap_1.mergeMap; },{"168":168,"213":213}],188:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var reduce_1 = require(215); Observable_1.Observable.prototype.reduce = reduce_1.reduce; },{"168":168,"215":215}],189:[function(require,module,exports){ "use strict"; var Observable_1 = require(168); var toArray_1 = require(216); Observable_1.Observable.prototype.toArray = toArray_1.toArray; },{"168":168,"216":216}],190:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Observable_1 = require(168); var ScalarObservable_1 = require(198); var EmptyObservable_1 = require(193); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var ArrayLikeObservable = (function (_super) { __extends(ArrayLikeObservable, _super); function ArrayLikeObservable(arrayLike, scheduler) { _super.call(this); this.arrayLike = arrayLike; this.scheduler = scheduler; if (!scheduler && arrayLike.length === 1) { this._isScalar = true; this.value = arrayLike[0]; } } ArrayLikeObservable.create = function (arrayLike, scheduler) { var length = arrayLike.length; if (length === 0) { return new EmptyObservable_1.EmptyObservable(); } else if (length === 1) { return new ScalarObservable_1.ScalarObservable(arrayLike[0], scheduler); } else { return new ArrayLikeObservable(arrayLike, scheduler); } }; ArrayLikeObservable.dispatch = function (state) { var arrayLike = state.arrayLike, index = state.index, length = state.length, subscriber = state.subscriber; if (subscriber.closed) { return; } if (index >= length) { subscriber.complete(); return; } subscriber.next(arrayLike[index]); state.index = index + 1; this.schedule(state); }; ArrayLikeObservable.prototype._subscribe = function (subscriber) { var index = 0; var _a = this, arrayLike = _a.arrayLike, scheduler = _a.scheduler; var length = arrayLike.length; if (scheduler) { return scheduler.schedule(ArrayLikeObservable.dispatch, 0, { arrayLike: arrayLike, index: index, length: length, subscriber: subscriber }); } else { for (var i = 0; i < length && !subscriber.closed; i++) { subscriber.next(arrayLike[i]); } subscriber.complete(); } }; return ArrayLikeObservable; }(Observable_1.Observable)); exports.ArrayLikeObservable = ArrayLikeObservable; },{"168":168,"193":193,"198":198}],191:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Observable_1 = require(168); var ScalarObservable_1 = require(198); var EmptyObservable_1 = require(193); var isScheduler_1 = require(232); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var ArrayObservable = (function (_super) { __extends(ArrayObservable, _super); function ArrayObservable(array, scheduler) { _super.call(this); this.array = array; this.scheduler = scheduler; if (!scheduler && array.length === 1) { this._isScalar = true; this.value = array[0]; } } ArrayObservable.create = function (array, scheduler) { return new ArrayObservable(array, scheduler); }; /** * Creates an Observable that emits some values you specify as arguments, * immediately one after the other, and then emits a complete notification. * * Emits the arguments you provide, then completes. * * * * * This static operator is useful for creating a simple Observable that only * emits the arguments given, and the complete notification thereafter. It can * be used for composing with other Observables, such as with {@link concat}. * By default, it uses a `null` IScheduler, which means the `next` * notifications are sent synchronously, although with a different IScheduler * it is possible to determine when those notifications will be delivered. * * @example Emit 10, 20, 30, then 'a', 'b', 'c', then start ticking every second. * var numbers = Rx.Observable.of(10, 20, 30); * var letters = Rx.Observable.of('a', 'b', 'c'); * var interval = Rx.Observable.interval(1000); * var result = numbers.concat(letters).concat(interval); * result.subscribe(x => console.log(x)); * * @see {@link create} * @see {@link empty} * @see {@link never} * @see {@link throw} * * @param {...T} values Arguments that represent `next` values to be emitted. * @param {Scheduler} [scheduler] A {@link IScheduler} to use for scheduling * the emissions of the `next` notifications. * @return {Observable} An Observable that emits each given input value. * @static true * @name of * @owner Observable */ ArrayObservable.of = function () { var array = []; for (var _i = 0; _i < arguments.length; _i++) { array[_i - 0] = arguments[_i]; } var scheduler = array[array.length - 1]; if (isScheduler_1.isScheduler(scheduler)) { array.pop(); } else { scheduler = null; } var len = array.length; if (len > 1) { return new ArrayObservable(array, scheduler); } else if (len === 1) { return new ScalarObservable_1.ScalarObservable(array[0], scheduler); } else { return new EmptyObservable_1.EmptyObservable(scheduler); } }; ArrayObservable.dispatch = function (state) { var array = state.array, index = state.index, count = state.count, subscriber = state.subscriber; if (index >= count) { subscriber.complete(); return; } subscriber.next(array[index]); if (subscriber.closed) { return; } state.index = index + 1; this.schedule(state); }; ArrayObservable.prototype._subscribe = function (subscriber) { var index = 0; var array = this.array; var count = array.length; var scheduler = this.scheduler; if (scheduler) { return scheduler.schedule(ArrayObservable.dispatch, 0, { array: array, index: index, count: count, subscriber: subscriber }); } else { for (var i = 0; i < count && !subscriber.closed; i++) { subscriber.next(array[i]); } subscriber.complete(); } }; return ArrayObservable; }(Observable_1.Observable)); exports.ArrayObservable = ArrayObservable; },{"168":168,"193":193,"198":198,"232":232}],192:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Observable_1 = require(168); var subscribeToResult_1 = require(234); var OuterSubscriber_1 = require(170); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var DeferObservable = (function (_super) { __extends(DeferObservable, _super); function DeferObservable(observableFactory) { _super.call(this); this.observableFactory = observableFactory; } /** * Creates an Observable that, on subscribe, calls an Observable factory to * make an Observable for each new Observer. * * Creates the Observable lazily, that is, only when it * is subscribed. * * * * * `defer` allows you to create the Observable only when the Observer * subscribes, and create a fresh Observable for each Observer. It waits until * an Observer subscribes to it, and then it generates an Observable, * typically with an Observable factory function. It does this afresh for each * subscriber, so although each subscriber may think it is subscribing to the * same Observable, in fact each subscriber gets its own individual * Observable. * * @example Subscribe to either an Observable of clicks or an Observable of interval, at random * var clicksOrInterval = Rx.Observable.defer(function () { * if (Math.random() > 0.5) { * return Rx.Observable.fromEvent(document, 'click'); * } else { * return Rx.Observable.interval(1000); * } * }); * clicksOrInterval.subscribe(x => console.log(x)); * * // Results in the following behavior: * // If the result of Math.random() is greater than 0.5 it will listen * // for clicks anywhere on the "document"; when document is clicked it * // will log a MouseEvent object to the console. If the result is less * // than 0.5 it will emit ascending numbers, one every second(1000ms). * * @see {@link create} * * @param {function(): Observable|Promise} observableFactory The Observable * factory function to invoke for each Observer that subscribes to the output * Observable. May also return a Promise, which will be converted on the fly * to an Observable. * @return {Observable} An Observable whose Observers' subscriptions trigger * an invocation of the given Observable factory function. * @static true * @name defer * @owner Observable */ DeferObservable.create = function (observableFactory) { return new DeferObservable(observableFactory); }; DeferObservable.prototype._subscribe = function (subscriber) { return new DeferSubscriber(subscriber, this.observableFactory); }; return DeferObservable; }(Observable_1.Observable)); exports.DeferObservable = DeferObservable; var DeferSubscriber = (function (_super) { __extends(DeferSubscriber, _super); function DeferSubscriber(destination, factory) { _super.call(this, destination); this.factory = factory; this.tryDefer(); } DeferSubscriber.prototype.tryDefer = function () { try { this._callFactory(); } catch (err) { this._error(err); } }; DeferSubscriber.prototype._callFactory = function () { var result = this.factory(); if (result) { this.add(subscribeToResult_1.subscribeToResult(this, result)); } }; return DeferSubscriber; }(OuterSubscriber_1.OuterSubscriber)); },{"168":168,"170":170,"234":234}],193:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Observable_1 = require(168); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var EmptyObservable = (function (_super) { __extends(EmptyObservable, _super); function EmptyObservable(scheduler) { _super.call(this); this.scheduler = scheduler; } /** * Creates an Observable that emits no items to the Observer and immediately * emits a complete notification. * * Just emits 'complete', and nothing else. * * * * * This static operator is useful for creating a simple Observable that only * emits the complete notification. It can be used for composing with other * Observables, such as in a {@link mergeMap}. * * @example Emit the number 7, then complete. * var result = Rx.Observable.empty().startWith(7); * result.subscribe(x => console.log(x)); * * @example Map and flatten only odd numbers to the sequence 'a', 'b', 'c' * var interval = Rx.Observable.interval(1000); * var result = interval.mergeMap(x => * x % 2 === 1 ? Rx.Observable.of('a', 'b', 'c') : Rx.Observable.empty() * ); * result.subscribe(x => console.log(x)); * * // Results in the following to the console: * // x is equal to the count on the interval eg(0,1,2,3,...) * // x will occur every 1000ms * // if x % 2 is equal to 1 print abc * // if x % 2 is not equal to 1 nothing will be output * * @see {@link create} * @see {@link never} * @see {@link of} * @see {@link throw} * * @param {Scheduler} [scheduler] A {@link IScheduler} to use for scheduling * the emission of the complete notification. * @return {Observable} An "empty" Observable: emits only the complete * notification. * @static true * @name empty * @owner Observable */ EmptyObservable.create = function (scheduler) { return new EmptyObservable(scheduler); }; EmptyObservable.dispatch = function (arg) { var subscriber = arg.subscriber; subscriber.complete(); }; EmptyObservable.prototype._subscribe = function (subscriber) { var scheduler = this.scheduler; if (scheduler) { return scheduler.schedule(EmptyObservable.dispatch, 0, { subscriber: subscriber }); } else { subscriber.complete(); } }; return EmptyObservable; }(Observable_1.Observable)); exports.EmptyObservable = EmptyObservable; },{"168":168}],194:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Observable_1 = require(168); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var ErrorObservable = (function (_super) { __extends(ErrorObservable, _super); function ErrorObservable(error, scheduler) { _super.call(this); this.error = error; this.scheduler = scheduler; } /** * Creates an Observable that emits no items to the Observer and immediately * emits an error notification. * * Just emits 'error', and nothing else. * * * * * This static operator is useful for creating a simple Observable that only * emits the error notification. It can be used for composing with other * Observables, such as in a {@link mergeMap}. * * @example Emit the number 7, then emit an error. * var result = Rx.Observable.throw(new Error('oops!')).startWith(7); * result.subscribe(x => console.log(x), e => console.error(e)); * * @example Map and flattens numbers to the sequence 'a', 'b', 'c', but throw an error for 13 * var interval = Rx.Observable.interval(1000); * var result = interval.mergeMap(x => * x === 13 ? * Rx.Observable.throw('Thirteens are bad') : * Rx.Observable.of('a', 'b', 'c') * ); * result.subscribe(x => console.log(x), e => console.error(e)); * * @see {@link create} * @see {@link empty} * @see {@link never} * @see {@link of} * * @param {any} error The particular Error to pass to the error notification. * @param {Scheduler} [scheduler] A {@link IScheduler} to use for scheduling * the emission of the error notification. * @return {Observable} An error Observable: emits only the error notification * using the given error argument. * @static true * @name throw * @owner Observable */ ErrorObservable.create = function (error, scheduler) { return new ErrorObservable(error, scheduler); }; ErrorObservable.dispatch = function (arg) { var error = arg.error, subscriber = arg.subscriber; subscriber.error(error); }; ErrorObservable.prototype._subscribe = function (subscriber) { var error = this.error; var scheduler = this.scheduler; if (scheduler) { return scheduler.schedule(ErrorObservable.dispatch, 0, { error: error, subscriber: subscriber }); } else { subscriber.error(error); } }; return ErrorObservable; }(Observable_1.Observable)); exports.ErrorObservable = ErrorObservable; },{"168":168}],195:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var isArray_1 = require(228); var isPromise_1 = require(231); var PromiseObservable_1 = require(197); var IteratorObservable_1 = require(196); var ArrayObservable_1 = require(191); var ArrayLikeObservable_1 = require(190); var iterator_1 = require(223); var Observable_1 = require(168); var observeOn_1 = require(214); var observable_1 = require(224); var isArrayLike = (function (x) { return x && typeof x.length === 'number'; }); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var FromObservable = (function (_super) { __extends(FromObservable, _super); function FromObservable(ish, scheduler) { _super.call(this, null); this.ish = ish; this.scheduler = scheduler; } /** * Creates an Observable from an Array, an array-like object, a Promise, an * iterable object, or an Observable-like object. * * Converts almost anything to an Observable. * * * * Convert various other objects and data types into Observables. `from` * converts a Promise or an array-like or an * [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterable) * object into an Observable that emits the items in that promise or array or * iterable. A String, in this context, is treated as an array of characters. * Observable-like objects (contains a function named with the ES2015 Symbol * for Observable) can also be converted through this operator. * * @example Converts an array to an Observable * var array = [10, 20, 30]; * var result = Rx.Observable.from(array); * result.subscribe(x => console.log(x)); * * // Results in the following: * // 10 20 30 * * @example Convert an infinite iterable (from a generator) to an Observable * function* generateDoubles(seed) { * var i = seed; * while (true) { * yield i; * i = 2 * i; // double it * } * } * * var iterator = generateDoubles(3); * var result = Rx.Observable.from(iterator).take(10); * result.subscribe(x => console.log(x)); * * // Results in the following: * // 3 6 12 24 48 96 192 384 768 1536 * * @see {@link create} * @see {@link fromEvent} * @see {@link fromEventPattern} * @see {@link fromPromise} * * @param {ObservableInput} ish A subscribable object, a Promise, an * Observable-like, an Array, an iterable or an array-like object to be * converted. * @param {Scheduler} [scheduler] The scheduler on which to schedule the * emissions of values. * @return {Observable} The Observable whose values are originally from the * input object that was converted. * @static true * @name from * @owner Observable */ FromObservable.create = function (ish, scheduler) { if (ish != null) { if (typeof ish[observable_1.$$observable] === 'function') { if (ish instanceof Observable_1.Observable && !scheduler) { return ish; } return new FromObservable(ish, scheduler); } else if (isArray_1.isArray(ish)) { return new ArrayObservable_1.ArrayObservable(ish, scheduler); } else if (isPromise_1.isPromise(ish)) { return new PromiseObservable_1.PromiseObservable(ish, scheduler); } else if (typeof ish[iterator_1.$$iterator] === 'function' || typeof ish === 'string') { return new IteratorObservable_1.IteratorObservable(ish, scheduler); } else if (isArrayLike(ish)) { return new ArrayLikeObservable_1.ArrayLikeObservable(ish, scheduler); } } throw new TypeError((ish !== null && typeof ish || ish) + ' is not observable'); }; FromObservable.prototype._subscribe = function (subscriber) { var ish = this.ish; var scheduler = this.scheduler; if (scheduler == null) { return ish[observable_1.$$observable]().subscribe(subscriber); } else { return ish[observable_1.$$observable]().subscribe(new observeOn_1.ObserveOnSubscriber(subscriber, scheduler, 0)); } }; return FromObservable; }(Observable_1.Observable)); exports.FromObservable = FromObservable; },{"168":168,"190":190,"191":191,"196":196,"197":197,"214":214,"223":223,"224":224,"228":228,"231":231}],196:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var root_1 = require(233); var Observable_1 = require(168); var iterator_1 = require(223); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var IteratorObservable = (function (_super) { __extends(IteratorObservable, _super); function IteratorObservable(iterator, scheduler) { _super.call(this); this.scheduler = scheduler; if (iterator == null) { throw new Error('iterator cannot be null.'); } this.iterator = getIterator(iterator); } IteratorObservable.create = function (iterator, scheduler) { return new IteratorObservable(iterator, scheduler); }; IteratorObservable.dispatch = function (state) { var index = state.index, hasError = state.hasError, iterator = state.iterator, subscriber = state.subscriber; if (hasError) { subscriber.error(state.error); return; } var result = iterator.next(); if (result.done) { subscriber.complete(); return; } subscriber.next(result.value); state.index = index + 1; if (subscriber.closed) { if (typeof iterator.return === 'function') { iterator.return(); } return; } this.schedule(state); }; IteratorObservable.prototype._subscribe = function (subscriber) { var index = 0; var _a = this, iterator = _a.iterator, scheduler = _a.scheduler; if (scheduler) { return scheduler.schedule(IteratorObservable.dispatch, 0, { index: index, iterator: iterator, subscriber: subscriber }); } else { do { var result = iterator.next(); if (result.done) { subscriber.complete(); break; } else { subscriber.next(result.value); } if (subscriber.closed) { if (typeof iterator.return === 'function') { iterator.return(); } break; } } while (true); } }; return IteratorObservable; }(Observable_1.Observable)); exports.IteratorObservable = IteratorObservable; var StringIterator = (function () { function StringIterator(str, idx, len) { if (idx === void 0) { idx = 0; } if (len === void 0) { len = str.length; } this.str = str; this.idx = idx; this.len = len; } StringIterator.prototype[iterator_1.$$iterator] = function () { return (this); }; StringIterator.prototype.next = function () { return this.idx < this.len ? { done: false, value: this.str.charAt(this.idx++) } : { done: true, value: undefined }; }; return StringIterator; }()); var ArrayIterator = (function () { function ArrayIterator(arr, idx, len) { if (idx === void 0) { idx = 0; } if (len === void 0) { len = toLength(arr); } this.arr = arr; this.idx = idx; this.len = len; } ArrayIterator.prototype[iterator_1.$$iterator] = function () { return this; }; ArrayIterator.prototype.next = function () { return this.idx < this.len ? { done: false, value: this.arr[this.idx++] } : { done: true, value: undefined }; }; return ArrayIterator; }()); function getIterator(obj) { var i = obj[iterator_1.$$iterator]; if (!i && typeof obj === 'string') { return new StringIterator(obj); } if (!i && obj.length !== undefined) { return new ArrayIterator(obj); } if (!i) { throw new TypeError('object is not iterable'); } return obj[iterator_1.$$iterator](); } var maxSafeInteger = Math.pow(2, 53) - 1; function toLength(o) { var len = +o.length; if (isNaN(len)) { return 0; } if (len === 0 || !numberIsFinite(len)) { return len; } len = sign(len) * Math.floor(Math.abs(len)); if (len <= 0) { return 0; } if (len > maxSafeInteger) { return maxSafeInteger; } return len; } function numberIsFinite(value) { return typeof value === 'number' && root_1.root.isFinite(value); } function sign(value) { var valueAsNumber = +value; if (valueAsNumber === 0) { return valueAsNumber; } if (isNaN(valueAsNumber)) { return valueAsNumber; } return valueAsNumber < 0 ? -1 : 1; } },{"168":168,"223":223,"233":233}],197:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var root_1 = require(233); var Observable_1 = require(168); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var PromiseObservable = (function (_super) { __extends(PromiseObservable, _super); function PromiseObservable(promise, scheduler) { _super.call(this); this.promise = promise; this.scheduler = scheduler; } /** * Converts a Promise to an Observable. * * Returns an Observable that just emits the Promise's * resolved value, then completes. * * Converts an ES2015 Promise or a Promises/A+ spec compliant Promise to an * Observable. If the Promise resolves with a value, the output Observable * emits that resolved value as a `next`, and then completes. If the Promise * is rejected, then the output Observable emits the corresponding Error. * * @example Convert the Promise returned by Fetch to an Observable * var result = Rx.Observable.fromPromise(fetch('http://myserver.com/')); * result.subscribe(x => console.log(x), e => console.error(e)); * * @see {@link bindCallback} * @see {@link from} * * @param {Promise} promise The promise to be converted. * @param {Scheduler} [scheduler] An optional IScheduler to use for scheduling * the delivery of the resolved value (or the rejection). * @return {Observable} An Observable which wraps the Promise. * @static true * @name fromPromise * @owner Observable */ PromiseObservable.create = function (promise, scheduler) { return new PromiseObservable(promise, scheduler); }; PromiseObservable.prototype._subscribe = function (subscriber) { var _this = this; var promise = this.promise; var scheduler = this.scheduler; if (scheduler == null) { if (this._isScalar) { if (!subscriber.closed) { subscriber.next(this.value); subscriber.complete(); } } else { promise.then(function (value) { _this.value = value; _this._isScalar = true; if (!subscriber.closed) { subscriber.next(value); subscriber.complete(); } }, function (err) { if (!subscriber.closed) { subscriber.error(err); } }) .then(null, function (err) { // escape the promise trap, throw unhandled errors root_1.root.setTimeout(function () { throw err; }); }); } } else { if (this._isScalar) { if (!subscriber.closed) { return scheduler.schedule(dispatchNext, 0, { value: this.value, subscriber: subscriber }); } } else { promise.then(function (value) { _this.value = value; _this._isScalar = true; if (!subscriber.closed) { subscriber.add(scheduler.schedule(dispatchNext, 0, { value: value, subscriber: subscriber })); } }, function (err) { if (!subscriber.closed) { subscriber.add(scheduler.schedule(dispatchError, 0, { err: err, subscriber: subscriber })); } }) .then(null, function (err) { // escape the promise trap, throw unhandled errors root_1.root.setTimeout(function () { throw err; }); }); } } }; return PromiseObservable; }(Observable_1.Observable)); exports.PromiseObservable = PromiseObservable; function dispatchNext(arg) { var value = arg.value, subscriber = arg.subscriber; if (!subscriber.closed) { subscriber.next(value); subscriber.complete(); } } function dispatchError(arg) { var err = arg.err, subscriber = arg.subscriber; if (!subscriber.closed) { subscriber.error(err); } } },{"168":168,"233":233}],198:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Observable_1 = require(168); /** * We need this JSDoc comment for affecting ESDoc. * @extends {Ignored} * @hide true */ var ScalarObservable = (function (_super) { __extends(ScalarObservable, _super); function ScalarObservable(value, scheduler) { _super.call(this); this.value = value; this.scheduler = scheduler; this._isScalar = true; if (scheduler) { this._isScalar = false; } } ScalarObservable.create = function (value, scheduler) { return new ScalarObservable(value, scheduler); }; ScalarObservable.dispatch = function (state) { var done = state.done, value = state.value, subscriber = state.subscriber; if (done) { subscriber.complete(); return; } subscriber.next(value); if (subscriber.closed) { return; } state.done = true; this.schedule(state); }; ScalarObservable.prototype._subscribe = function (subscriber) { var value = this.value; var scheduler = this.scheduler; if (scheduler) { return scheduler.schedule(ScalarObservable.dispatch, 0, { done: false, value: value, subscriber: subscriber }); } else { subscriber.next(value); if (!subscriber.closed) { subscriber.complete(); } } }; return ScalarObservable; }(Observable_1.Observable)); exports.ScalarObservable = ScalarObservable; },{"168":168}],199:[function(require,module,exports){ "use strict"; var DeferObservable_1 = require(192); exports.defer = DeferObservable_1.DeferObservable.create; },{"192":192}],200:[function(require,module,exports){ "use strict"; var EmptyObservable_1 = require(193); exports.empty = EmptyObservable_1.EmptyObservable.create; },{"193":193}],201:[function(require,module,exports){ "use strict"; var FromObservable_1 = require(195); exports.from = FromObservable_1.FromObservable.create; },{"195":195}],202:[function(require,module,exports){ "use strict"; var ArrayObservable_1 = require(191); exports.of = ArrayObservable_1.ArrayObservable.of; },{"191":191}],203:[function(require,module,exports){ "use strict"; var ErrorObservable_1 = require(194); exports._throw = ErrorObservable_1.ErrorObservable.create; },{"194":194}],204:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var OuterSubscriber_1 = require(170); var subscribeToResult_1 = require(234); /** * Catches errors on the observable to be handled by returning a new observable or throwing an error. * * * * @example Continues with a different Observable when there's an error * * Observable.of(1, 2, 3, 4, 5) * .map(n => { * if (n == 4) { * throw 'four!'; * } * return n; * }) * .catch(err => Observable.of('I', 'II', 'III', 'IV', 'V')) * .subscribe(x => console.log(x)); * // 1, 2, 3, I, II, III, IV, V * * @example Retries the caught source Observable again in case of error, similar to retry() operator * * Observable.of(1, 2, 3, 4, 5) * .map(n => { * if (n === 4) { * throw 'four!'; * } * return n; * }) * .catch((err, caught) => caught) * .take(30) * .subscribe(x => console.log(x)); * // 1, 2, 3, 1, 2, 3, ... * * @example Throws a new error when the source Observable throws an error * * Observable.of(1, 2, 3, 4, 5) * .map(n => { * if (n == 4) { * throw 'four!'; * } * return n; * }) * .catch(err => { * throw 'error in source. Details: ' + err; * }) * .subscribe( * x => console.log(x), * err => console.log(err) * ); * // 1, 2, 3, error in source. Details: four! * * @param {function} selector a function that takes as arguments `err`, which is the error, and `caught`, which * is the source observable, in case you'd like to "retry" that observable by returning it again. Whatever observable * is returned by the `selector` will be used to continue the observable chain. * @return {Observable} an observable that originates from either the source or the observable returned by the * catch `selector` function. * @method catch * @name catch * @owner Observable */ function _catch(selector) { var operator = new CatchOperator(selector); var caught = this.lift(operator); return (operator.caught = caught); } exports._catch = _catch; var CatchOperator = (function () { function CatchOperator(selector) { this.selector = selector; } CatchOperator.prototype.call = function (subscriber, source) { return source.subscribe(new CatchSubscriber(subscriber, this.selector, this.caught)); }; return CatchOperator; }()); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var CatchSubscriber = (function (_super) { __extends(CatchSubscriber, _super); function CatchSubscriber(destination, selector, caught) { _super.call(this, destination); this.selector = selector; this.caught = caught; } // NOTE: overriding `error` instead of `_error` because we don't want // to have this flag this subscriber as `isStopped`. We can mimic the // behavior of the RetrySubscriber (from the `retry` operator), where // we unsubscribe from our source chain, reset our Subscriber flags, // then subscribe to the selector result. CatchSubscriber.prototype.error = function (err) { if (!this.isStopped) { var result = void 0; try { result = this.selector(err, this.caught); } catch (err2) { _super.prototype.error.call(this, err2); return; } this._unsubscribeAndRecycle(); this.add(subscribeToResult_1.subscribeToResult(this, result)); } }; return CatchSubscriber; }(OuterSubscriber_1.OuterSubscriber)); },{"170":170,"234":234}],205:[function(require,module,exports){ "use strict"; var isScheduler_1 = require(232); var ArrayObservable_1 = require(191); var mergeAll_1 = require(212); /* tslint:disable:max-line-length */ /** * Creates an output Observable which sequentially emits all values from every * given input Observable after the current Observable. * * Concatenates multiple Observables together by * sequentially emitting their values, one Observable after the other. * * * * Joins this Observable with multiple other Observables by subscribing to them * one at a time, starting with the source, and merging their results into the * output Observable. Will wait for each Observable to complete before moving * on to the next. * * @example Concatenate a timer counting from 0 to 3 with a synchronous sequence from 1 to 10 * var timer = Rx.Observable.interval(1000).take(4); * var sequence = Rx.Observable.range(1, 10); * var result = timer.concat(sequence); * result.subscribe(x => console.log(x)); * * // results in: * // 1000ms-> 0 -1000ms-> 1 -1000ms-> 2 -1000ms-> 3 -immediate-> 1 ... 10 * * @example Concatenate 3 Observables * var timer1 = Rx.Observable.interval(1000).take(10); * var timer2 = Rx.Observable.interval(2000).take(6); * var timer3 = Rx.Observable.interval(500).take(10); * var result = timer1.concat(timer2, timer3); * result.subscribe(x => console.log(x)); * * // results in the following: * // (Prints to console sequentially) * // -1000ms-> 0 -1000ms-> 1 -1000ms-> ... 9 * // -2000ms-> 0 -2000ms-> 1 -2000ms-> ... 5 * // -500ms-> 0 -500ms-> 1 -500ms-> ... 9 * * @see {@link concatAll} * @see {@link concatMap} * @see {@link concatMapTo} * * @param {Observable} other An input Observable to concatenate after the source * Observable. More than one input Observables may be given as argument. * @param {Scheduler} [scheduler=null] An optional IScheduler to schedule each * Observable subscription on. * @return {Observable} All values of each passed Observable merged into a * single Observable, in order, in serial fashion. * @method concat * @owner Observable */ function concat() { var observables = []; for (var _i = 0; _i < arguments.length; _i++) { observables[_i - 0] = arguments[_i]; } return this.lift.call(concatStatic.apply(void 0, [this].concat(observables))); } exports.concat = concat; /* tslint:enable:max-line-length */ /** * Creates an output Observable which sequentially emits all values from every * given input Observable after the current Observable. * * Concatenates multiple Observables together by * sequentially emitting their values, one Observable after the other. * * * * Joins multiple Observables together by subscribing to them one at a time and * merging their results into the output Observable. Will wait for each * Observable to complete before moving on to the next. * * @example Concatenate a timer counting from 0 to 3 with a synchronous sequence from 1 to 10 * var timer = Rx.Observable.interval(1000).take(4); * var sequence = Rx.Observable.range(1, 10); * var result = Rx.Observable.concat(timer, sequence); * result.subscribe(x => console.log(x)); * * // results in: * // 0 -1000ms-> 1 -1000ms-> 2 -1000ms-> 3 -immediate-> 1 ... 10 * * @example Concatenate 3 Observables * var timer1 = Rx.Observable.interval(1000).take(10); * var timer2 = Rx.Observable.interval(2000).take(6); * var timer3 = Rx.Observable.interval(500).take(10); * var result = Rx.Observable.concat(timer1, timer2, timer3); * result.subscribe(x => console.log(x)); * * // results in the following: * // (Prints to console sequentially) * // -1000ms-> 0 -1000ms-> 1 -1000ms-> ... 9 * // -2000ms-> 0 -2000ms-> 1 -2000ms-> ... 5 * // -500ms-> 0 -500ms-> 1 -500ms-> ... 9 * * @see {@link concatAll} * @see {@link concatMap} * @see {@link concatMapTo} * * @param {Observable} input1 An input Observable to concatenate with others. * @param {Observable} input2 An input Observable to concatenate with others. * More than one input Observables may be given as argument. * @param {Scheduler} [scheduler=null] An optional IScheduler to schedule each * Observable subscription on. * @return {Observable} All values of each passed Observable merged into a * single Observable, in order, in serial fashion. * @static true * @name concat * @owner Observable */ function concatStatic() { var observables = []; for (var _i = 0; _i < arguments.length; _i++) { observables[_i - 0] = arguments[_i]; } var scheduler = null; var args = observables; if (isScheduler_1.isScheduler(args[observables.length - 1])) { scheduler = args.pop(); } if (scheduler === null && observables.length === 1) { return observables[0]; } return new ArrayObservable_1.ArrayObservable(observables, scheduler).lift(new mergeAll_1.MergeAllOperator(1)); } exports.concatStatic = concatStatic; },{"191":191,"212":212,"232":232}],206:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /* tslint:disable:max-line-length */ /** * Emits a given value if the source Observable completes without emitting any * `next` value, otherwise mirrors the source Observable. * * If the source Observable turns out to be empty, then * this operator will emit a default value. * * * * `defaultIfEmpty` emits the values emitted by the source Observable or a * specified default value if the source Observable is empty (completes without * having emitted any `next` value). * * @example If no clicks happen in 5 seconds, then emit "no clicks" * var clicks = Rx.Observable.fromEvent(document, 'click'); * var clicksBeforeFive = clicks.takeUntil(Rx.Observable.interval(5000)); * var result = clicksBeforeFive.defaultIfEmpty('no clicks'); * result.subscribe(x => console.log(x)); * * @see {@link empty} * @see {@link last} * * @param {any} [defaultValue=null] The default value used if the source * Observable is empty. * @return {Observable} An Observable that emits either the specified * `defaultValue` if the source Observable emits no items, or the values emitted * by the source Observable. * @method defaultIfEmpty * @owner Observable */ function defaultIfEmpty(defaultValue) { if (defaultValue === void 0) { defaultValue = null; } return this.lift(new DefaultIfEmptyOperator(defaultValue)); } exports.defaultIfEmpty = defaultIfEmpty; var DefaultIfEmptyOperator = (function () { function DefaultIfEmptyOperator(defaultValue) { this.defaultValue = defaultValue; } DefaultIfEmptyOperator.prototype.call = function (subscriber, source) { return source.subscribe(new DefaultIfEmptySubscriber(subscriber, this.defaultValue)); }; return DefaultIfEmptyOperator; }()); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var DefaultIfEmptySubscriber = (function (_super) { __extends(DefaultIfEmptySubscriber, _super); function DefaultIfEmptySubscriber(destination, defaultValue) { _super.call(this, destination); this.defaultValue = defaultValue; this.isEmpty = true; } DefaultIfEmptySubscriber.prototype._next = function (value) { this.isEmpty = false; this.destination.next(value); }; DefaultIfEmptySubscriber.prototype._complete = function () { if (this.isEmpty) { this.destination.next(this.defaultValue); } this.destination.complete(); }; return DefaultIfEmptySubscriber; }(Subscriber_1.Subscriber)); },{"172":172}],207:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /* tslint:disable:max-line-length */ /** * Perform a side effect for every emission on the source Observable, but return * an Observable that is identical to the source. * * Intercepts each emission on the source and runs a * function, but returns an output which is identical to the source. * * * * Returns a mirrored Observable of the source Observable, but modified so that * the provided Observer is called to perform a side effect for every value, * error, and completion emitted by the source. Any errors that are thrown in * the aforementioned Observer or handlers are safely sent down the error path * of the output Observable. * * This operator is useful for debugging your Observables for the correct values * or performing other side effects. * * Note: this is different to a `subscribe` on the Observable. If the Observable * returned by `do` is not subscribed, the side effects specified by the * Observer will never happen. `do` therefore simply spies on existing * execution, it does not trigger an execution to happen like `subscribe` does. * * @example Map every every click to the clientX position of that click, while also logging the click event * var clicks = Rx.Observable.fromEvent(document, 'click'); * var positions = clicks * .do(ev => console.log(ev)) * .map(ev => ev.clientX); * positions.subscribe(x => console.log(x)); * * @see {@link map} * @see {@link subscribe} * * @param {Observer|function} [nextOrObserver] A normal Observer object or a * callback for `next`. * @param {function} [error] Callback for errors in the source. * @param {function} [complete] Callback for the completion of the source. * @return {Observable} An Observable identical to the source, but runs the * specified Observer or callback(s) for each item. * @method do * @name do * @owner Observable */ function _do(nextOrObserver, error, complete) { return this.lift(new DoOperator(nextOrObserver, error, complete)); } exports._do = _do; var DoOperator = (function () { function DoOperator(nextOrObserver, error, complete) { this.nextOrObserver = nextOrObserver; this.error = error; this.complete = complete; } DoOperator.prototype.call = function (subscriber, source) { return source.subscribe(new DoSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); }; return DoOperator; }()); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var DoSubscriber = (function (_super) { __extends(DoSubscriber, _super); function DoSubscriber(destination, nextOrObserver, error, complete) { _super.call(this, destination); var safeSubscriber = new Subscriber_1.Subscriber(nextOrObserver, error, complete); safeSubscriber.syncErrorThrowable = true; this.add(safeSubscriber); this.safeSubscriber = safeSubscriber; } DoSubscriber.prototype._next = function (value) { var safeSubscriber = this.safeSubscriber; safeSubscriber.next(value); if (safeSubscriber.syncErrorThrown) { this.destination.error(safeSubscriber.syncErrorValue); } else { this.destination.next(value); } }; DoSubscriber.prototype._error = function (err) { var safeSubscriber = this.safeSubscriber; safeSubscriber.error(err); if (safeSubscriber.syncErrorThrown) { this.destination.error(safeSubscriber.syncErrorValue); } else { this.destination.error(err); } }; DoSubscriber.prototype._complete = function () { var safeSubscriber = this.safeSubscriber; safeSubscriber.complete(); if (safeSubscriber.syncErrorThrown) { this.destination.error(safeSubscriber.syncErrorValue); } else { this.destination.complete(); } }; return DoSubscriber; }(Subscriber_1.Subscriber)); },{"172":172}],208:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var tryCatch_1 = require(236); var errorObject_1 = require(227); var OuterSubscriber_1 = require(170); var subscribeToResult_1 = require(234); /* tslint:disable:max-line-length */ /** * Recursively projects each source value to an Observable which is merged in * the output Observable. * * It's similar to {@link mergeMap}, but applies the * projection function to every source value as well as every output value. * It's recursive. * * * * Returns an Observable that emits items based on applying a function that you * supply to each item emitted by the source Observable, where that function * returns an Observable, and then merging those resulting Observables and * emitting the results of this merger. *Expand* will re-emit on the output * Observable every source value. Then, each output value is given to the * `project` function which returns an inner Observable to be merged on the * output Observable. Those output values resulting from the projection are also * given to the `project` function to produce new output values. This is how * *expand* behaves recursively. * * @example Start emitting the powers of two on every click, at most 10 of them * var clicks = Rx.Observable.fromEvent(document, 'click'); * var powersOfTwo = clicks * .mapTo(1) * .expand(x => Rx.Observable.of(2 * x).delay(1000)) * .take(10); * powersOfTwo.subscribe(x => console.log(x)); * * @see {@link mergeMap} * @see {@link mergeScan} * * @param {function(value: T, index: number) => Observable} project A function * that, when applied to an item emitted by the source or the output Observable, * returns an Observable. * @param {number} [concurrent=Number.POSITIVE_INFINITY] Maximum number of input * Observables being subscribed to concurrently. * @param {Scheduler} [scheduler=null] The IScheduler to use for subscribing to * each projected inner Observable. * @return {Observable} An Observable that emits the source values and also * result of applying the projection function to each value emitted on the * output Observable and and merging the results of the Observables obtained * from this transformation. * @method expand * @owner Observable */ function expand(project, concurrent, scheduler) { if (concurrent === void 0) { concurrent = Number.POSITIVE_INFINITY; } if (scheduler === void 0) { scheduler = undefined; } concurrent = (concurrent || 0) < 1 ? Number.POSITIVE_INFINITY : concurrent; return this.lift(new ExpandOperator(project, concurrent, scheduler)); } exports.expand = expand; var ExpandOperator = (function () { function ExpandOperator(project, concurrent, scheduler) { this.project = project; this.concurrent = concurrent; this.scheduler = scheduler; } ExpandOperator.prototype.call = function (subscriber, source) { return source.subscribe(new ExpandSubscriber(subscriber, this.project, this.concurrent, this.scheduler)); }; return ExpandOperator; }()); exports.ExpandOperator = ExpandOperator; /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var ExpandSubscriber = (function (_super) { __extends(ExpandSubscriber, _super); function ExpandSubscriber(destination, project, concurrent, scheduler) { _super.call(this, destination); this.project = project; this.concurrent = concurrent; this.scheduler = scheduler; this.index = 0; this.active = 0; this.hasCompleted = false; if (concurrent < Number.POSITIVE_INFINITY) { this.buffer = []; } } ExpandSubscriber.dispatch = function (arg) { var subscriber = arg.subscriber, result = arg.result, value = arg.value, index = arg.index; subscriber.subscribeToProjection(result, value, index); }; ExpandSubscriber.prototype._next = function (value) { var destination = this.destination; if (destination.closed) { this._complete(); return; } var index = this.index++; if (this.active < this.concurrent) { destination.next(value); var result = tryCatch_1.tryCatch(this.project)(value, index); if (result === errorObject_1.errorObject) { destination.error(errorObject_1.errorObject.e); } else if (!this.scheduler) { this.subscribeToProjection(result, value, index); } else { var state = { subscriber: this, result: result, value: value, index: index }; this.add(this.scheduler.schedule(ExpandSubscriber.dispatch, 0, state)); } } else { this.buffer.push(value); } }; ExpandSubscriber.prototype.subscribeToProjection = function (result, value, index) { this.active++; this.add(subscribeToResult_1.subscribeToResult(this, result, value, index)); }; ExpandSubscriber.prototype._complete = function () { this.hasCompleted = true; if (this.hasCompleted && this.active === 0) { this.destination.complete(); } }; ExpandSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { this._next(innerValue); }; ExpandSubscriber.prototype.notifyComplete = function (innerSub) { var buffer = this.buffer; this.remove(innerSub); this.active--; if (buffer && buffer.length > 0) { this._next(buffer.shift()); } if (this.hasCompleted && this.active === 0) { this.destination.complete(); } }; return ExpandSubscriber; }(OuterSubscriber_1.OuterSubscriber)); exports.ExpandSubscriber = ExpandSubscriber; },{"170":170,"227":227,"234":234,"236":236}],209:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /* tslint:disable:max-line-length */ /** * Filter items emitted by the source Observable by only emitting those that * satisfy a specified predicate. * * Like * [Array.prototype.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), * it only emits a value from the source if it passes a criterion function. * * * * Similar to the well-known `Array.prototype.filter` method, this operator * takes values from the source Observable, passes them through a `predicate` * function and only emits those values that yielded `true`. * * @example Emit only click events whose target was a DIV element * var clicks = Rx.Observable.fromEvent(document, 'click'); * var clicksOnDivs = clicks.filter(ev => ev.target.tagName === 'DIV'); * clicksOnDivs.subscribe(x => console.log(x)); * * @see {@link distinct} * @see {@link distinctUntilChanged} * @see {@link distinctUntilKeyChanged} * @see {@link ignoreElements} * @see {@link partition} * @see {@link skip} * * @param {function(value: T, index: number): boolean} predicate A function that * evaluates each value emitted by the source Observable. If it returns `true`, * the value is emitted, if `false` the value is not passed to the output * Observable. The `index` parameter is the number `i` for the i-th source * emission that has happened since the subscription, starting from the number * `0`. * @param {any} [thisArg] An optional argument to determine the value of `this` * in the `predicate` function. * @return {Observable} An Observable of values from the source that were * allowed by the `predicate` function. * @method filter * @owner Observable */ function filter(predicate, thisArg) { return this.lift(new FilterOperator(predicate, thisArg)); } exports.filter = filter; var FilterOperator = (function () { function FilterOperator(predicate, thisArg) { this.predicate = predicate; this.thisArg = thisArg; } FilterOperator.prototype.call = function (subscriber, source) { return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg)); }; return FilterOperator; }()); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var FilterSubscriber = (function (_super) { __extends(FilterSubscriber, _super); function FilterSubscriber(destination, predicate, thisArg) { _super.call(this, destination); this.predicate = predicate; this.thisArg = thisArg; this.count = 0; this.predicate = predicate; } // the try catch block below is left specifically for // optimization and perf reasons. a tryCatcher is not necessary here. FilterSubscriber.prototype._next = function (value) { var result; try { result = this.predicate.call(this.thisArg, value, this.count++); } catch (err) { this.destination.error(err); return; } if (result) { this.destination.next(value); } }; return FilterSubscriber; }(Subscriber_1.Subscriber)); },{"172":172}],210:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /** * Applies a given `project` function to each value emitted by the source * Observable, and emits the resulting values as an Observable. * * Like [Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), * it passes each source value through a transformation function to get * corresponding output values. * * * * Similar to the well known `Array.prototype.map` function, this operator * applies a projection to each value and emits that projection in the output * Observable. * * @example Map every every click to the clientX position of that click * var clicks = Rx.Observable.fromEvent(document, 'click'); * var positions = clicks.map(ev => ev.clientX); * positions.subscribe(x => console.log(x)); * * @see {@link mapTo} * @see {@link pluck} * * @param {function(value: T, index: number): R} project The function to apply * to each `value` emitted by the source Observable. The `index` parameter is * the number `i` for the i-th emission that has happened since the * subscription, starting from the number `0`. * @param {any} [thisArg] An optional argument to define what `this` is in the * `project` function. * @return {Observable} An Observable that emits the values from the source * Observable transformed by the given `project` function. * @method map * @owner Observable */ function map(project, thisArg) { if (typeof project !== 'function') { throw new TypeError('argument is not a function. Are you looking for `mapTo()`?'); } return this.lift(new MapOperator(project, thisArg)); } exports.map = map; var MapOperator = (function () { function MapOperator(project, thisArg) { this.project = project; this.thisArg = thisArg; } MapOperator.prototype.call = function (subscriber, source) { return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg)); }; return MapOperator; }()); exports.MapOperator = MapOperator; /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var MapSubscriber = (function (_super) { __extends(MapSubscriber, _super); function MapSubscriber(destination, project, thisArg) { _super.call(this, destination); this.project = project; this.count = 0; this.thisArg = thisArg || this; } // NOTE: This looks unoptimized, but it's actually purposefully NOT // using try/catch optimizations. MapSubscriber.prototype._next = function (value) { var result; try { result = this.project.call(this.thisArg, value, this.count++); } catch (err) { this.destination.error(err); return; } this.destination.next(result); }; return MapSubscriber; }(Subscriber_1.Subscriber)); },{"172":172}],211:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); var Notification_1 = require(167); /** * Represents all of the notifications from the source Observable as `next` * emissions marked with their original types within {@link Notification} * objects. * * Wraps `next`, `error` and `complete` emissions in * {@link Notification} objects, emitted as `next` on the output Observable. * * * * * `materialize` returns an Observable that emits a `next` notification for each * `next`, `error`, or `complete` emission of the source Observable. When the * source Observable emits `complete`, the output Observable will emit `next` as * a Notification of type "complete", and then it will emit `complete` as well. * When the source Observable emits `error`, the output will emit `next` as a * Notification of type "error", and then `complete`. * * This operator is useful for producing metadata of the source Observable, to * be consumed as `next` emissions. Use it in conjunction with * {@link dematerialize}. * * @example Convert a faulty Observable to an Observable of Notifications * var letters = Rx.Observable.of('a', 'b', 13, 'd'); * var upperCase = letters.map(x => x.toUpperCase()); * var materialized = upperCase.materialize(); * materialized.subscribe(x => console.log(x)); * * // Results in the following: * // - Notification {kind: "N", value: "A", error: undefined, hasValue: true} * // - Notification {kind: "N", value: "B", error: undefined, hasValue: true} * // - Notification {kind: "E", value: undefined, error: TypeError: * // x.toUpperCase is not a function at MapSubscriber.letters.map.x * // [as project] (http://1…, hasValue: false} * * @see {@link Notification} * @see {@link dematerialize} * * @return {Observable>} An Observable that emits * {@link Notification} objects that wrap the original emissions from the source * Observable with metadata. * @method materialize * @owner Observable */ function materialize() { return this.lift(new MaterializeOperator()); } exports.materialize = materialize; var MaterializeOperator = (function () { function MaterializeOperator() { } MaterializeOperator.prototype.call = function (subscriber, source) { return source.subscribe(new MaterializeSubscriber(subscriber)); }; return MaterializeOperator; }()); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var MaterializeSubscriber = (function (_super) { __extends(MaterializeSubscriber, _super); function MaterializeSubscriber(destination) { _super.call(this, destination); } MaterializeSubscriber.prototype._next = function (value) { this.destination.next(Notification_1.Notification.createNext(value)); }; MaterializeSubscriber.prototype._error = function (err) { var destination = this.destination; destination.next(Notification_1.Notification.createError(err)); destination.complete(); }; MaterializeSubscriber.prototype._complete = function () { var destination = this.destination; destination.next(Notification_1.Notification.createComplete()); destination.complete(); }; return MaterializeSubscriber; }(Subscriber_1.Subscriber)); },{"167":167,"172":172}],212:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var OuterSubscriber_1 = require(170); var subscribeToResult_1 = require(234); /** * Converts a higher-order Observable into a first-order Observable which * concurrently delivers all values that are emitted on the inner Observables. * * Flattens an Observable-of-Observables. * * * * `mergeAll` subscribes to an Observable that emits Observables, also known as * a higher-order Observable. Each time it observes one of these emitted inner * Observables, it subscribes to that and delivers all the values from the * inner Observable on the output Observable. The output Observable only * completes once all inner Observables have completed. Any error delivered by * a inner Observable will be immediately emitted on the output Observable. * * @example Spawn a new interval Observable for each click event, and blend their outputs as one Observable * var clicks = Rx.Observable.fromEvent(document, 'click'); * var higherOrder = clicks.map((ev) => Rx.Observable.interval(1000)); * var firstOrder = higherOrder.mergeAll(); * firstOrder.subscribe(x => console.log(x)); * * @example Count from 0 to 9 every second for each click, but only allow 2 concurrent timers * var clicks = Rx.Observable.fromEvent(document, 'click'); * var higherOrder = clicks.map((ev) => Rx.Observable.interval(1000).take(10)); * var firstOrder = higherOrder.mergeAll(2); * firstOrder.subscribe(x => console.log(x)); * * @see {@link combineAll} * @see {@link concatAll} * @see {@link exhaust} * @see {@link merge} * @see {@link mergeMap} * @see {@link mergeMapTo} * @see {@link mergeScan} * @see {@link switch} * @see {@link zipAll} * * @param {number} [concurrent=Number.POSITIVE_INFINITY] Maximum number of inner * Observables being subscribed to concurrently. * @return {Observable} An Observable that emits values coming from all the * inner Observables emitted by the source Observable. * @method mergeAll * @owner Observable */ function mergeAll(concurrent) { if (concurrent === void 0) { concurrent = Number.POSITIVE_INFINITY; } return this.lift(new MergeAllOperator(concurrent)); } exports.mergeAll = mergeAll; var MergeAllOperator = (function () { function MergeAllOperator(concurrent) { this.concurrent = concurrent; } MergeAllOperator.prototype.call = function (observer, source) { return source.subscribe(new MergeAllSubscriber(observer, this.concurrent)); }; return MergeAllOperator; }()); exports.MergeAllOperator = MergeAllOperator; /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var MergeAllSubscriber = (function (_super) { __extends(MergeAllSubscriber, _super); function MergeAllSubscriber(destination, concurrent) { _super.call(this, destination); this.concurrent = concurrent; this.hasCompleted = false; this.buffer = []; this.active = 0; } MergeAllSubscriber.prototype._next = function (observable) { if (this.active < this.concurrent) { this.active++; this.add(subscribeToResult_1.subscribeToResult(this, observable)); } else { this.buffer.push(observable); } }; MergeAllSubscriber.prototype._complete = function () { this.hasCompleted = true; if (this.active === 0 && this.buffer.length === 0) { this.destination.complete(); } }; MergeAllSubscriber.prototype.notifyComplete = function (innerSub) { var buffer = this.buffer; this.remove(innerSub); this.active--; if (buffer.length > 0) { this._next(buffer.shift()); } else if (this.active === 0 && this.hasCompleted) { this.destination.complete(); } }; return MergeAllSubscriber; }(OuterSubscriber_1.OuterSubscriber)); exports.MergeAllSubscriber = MergeAllSubscriber; },{"170":170,"234":234}],213:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var subscribeToResult_1 = require(234); var OuterSubscriber_1 = require(170); /* tslint:disable:max-line-length */ /** * Projects each source value to an Observable which is merged in the output * Observable. * * Maps each value to an Observable, then flattens all of * these inner Observables using {@link mergeAll}. * * * * Returns an Observable that emits items based on applying a function that you * supply to each item emitted by the source Observable, where that function * returns an Observable, and then merging those resulting Observables and * emitting the results of this merger. * * @example Map and flatten each letter to an Observable ticking every 1 second * var letters = Rx.Observable.of('a', 'b', 'c'); * var result = letters.mergeMap(x => * Rx.Observable.interval(1000).map(i => x+i) * ); * result.subscribe(x => console.log(x)); * * // Results in the following: * // a0 * // b0 * // c0 * // a1 * // b1 * // c1 * // continues to list a,b,c with respective ascending integers * * @see {@link concatMap} * @see {@link exhaustMap} * @see {@link merge} * @see {@link mergeAll} * @see {@link mergeMapTo} * @see {@link mergeScan} * @see {@link switchMap} * * @param {function(value: T, ?index: number): Observable} project A function * that, when applied to an item emitted by the source Observable, returns an * Observable. * @param {function(outerValue: T, innerValue: I, outerIndex: number, innerIndex: number): any} [resultSelector] * A function to produce the value on the output Observable based on the values * and the indices of the source (outer) emission and the inner Observable * emission. The arguments passed to this function are: * - `outerValue`: the value that came from the source * - `innerValue`: the value that came from the projected Observable * - `outerIndex`: the "index" of the value that came from the source * - `innerIndex`: the "index" of the value from the projected Observable * @param {number} [concurrent=Number.POSITIVE_INFINITY] Maximum number of input * Observables being subscribed to concurrently. * @return {Observable} An Observable that emits the result of applying the * projection function (and the optional `resultSelector`) to each item emitted * by the source Observable and merging the results of the Observables obtained * from this transformation. * @method mergeMap * @owner Observable */ function mergeMap(project, resultSelector, concurrent) { if (concurrent === void 0) { concurrent = Number.POSITIVE_INFINITY; } if (typeof resultSelector === 'number') { concurrent = resultSelector; resultSelector = null; } return this.lift(new MergeMapOperator(project, resultSelector, concurrent)); } exports.mergeMap = mergeMap; var MergeMapOperator = (function () { function MergeMapOperator(project, resultSelector, concurrent) { if (concurrent === void 0) { concurrent = Number.POSITIVE_INFINITY; } this.project = project; this.resultSelector = resultSelector; this.concurrent = concurrent; } MergeMapOperator.prototype.call = function (observer, source) { return source.subscribe(new MergeMapSubscriber(observer, this.project, this.resultSelector, this.concurrent)); }; return MergeMapOperator; }()); exports.MergeMapOperator = MergeMapOperator; /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var MergeMapSubscriber = (function (_super) { __extends(MergeMapSubscriber, _super); function MergeMapSubscriber(destination, project, resultSelector, concurrent) { if (concurrent === void 0) { concurrent = Number.POSITIVE_INFINITY; } _super.call(this, destination); this.project = project; this.resultSelector = resultSelector; this.concurrent = concurrent; this.hasCompleted = false; this.buffer = []; this.active = 0; this.index = 0; } MergeMapSubscriber.prototype._next = function (value) { if (this.active < this.concurrent) { this._tryNext(value); } else { this.buffer.push(value); } }; MergeMapSubscriber.prototype._tryNext = function (value) { var result; var index = this.index++; try { result = this.project(value, index); } catch (err) { this.destination.error(err); return; } this.active++; this._innerSub(result, value, index); }; MergeMapSubscriber.prototype._innerSub = function (ish, value, index) { this.add(subscribeToResult_1.subscribeToResult(this, ish, value, index)); }; MergeMapSubscriber.prototype._complete = function () { this.hasCompleted = true; if (this.active === 0 && this.buffer.length === 0) { this.destination.complete(); } }; MergeMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { if (this.resultSelector) { this._notifyResultSelector(outerValue, innerValue, outerIndex, innerIndex); } else { this.destination.next(innerValue); } }; MergeMapSubscriber.prototype._notifyResultSelector = function (outerValue, innerValue, outerIndex, innerIndex) { var result; try { result = this.resultSelector(outerValue, innerValue, outerIndex, innerIndex); } catch (err) { this.destination.error(err); return; } this.destination.next(result); }; MergeMapSubscriber.prototype.notifyComplete = function (innerSub) { var buffer = this.buffer; this.remove(innerSub); this.active--; if (buffer.length > 0) { this._next(buffer.shift()); } else if (this.active === 0 && this.hasCompleted) { this.destination.complete(); } }; return MergeMapSubscriber; }(OuterSubscriber_1.OuterSubscriber)); exports.MergeMapSubscriber = MergeMapSubscriber; },{"170":170,"234":234}],214:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); var Notification_1 = require(167); /** * @see {@link Notification} * * @param scheduler * @param delay * @return {Observable|WebSocketSubject|Observable} * @method observeOn * @owner Observable */ function observeOn(scheduler, delay) { if (delay === void 0) { delay = 0; } return this.lift(new ObserveOnOperator(scheduler, delay)); } exports.observeOn = observeOn; var ObserveOnOperator = (function () { function ObserveOnOperator(scheduler, delay) { if (delay === void 0) { delay = 0; } this.scheduler = scheduler; this.delay = delay; } ObserveOnOperator.prototype.call = function (subscriber, source) { return source.subscribe(new ObserveOnSubscriber(subscriber, this.scheduler, this.delay)); }; return ObserveOnOperator; }()); exports.ObserveOnOperator = ObserveOnOperator; /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var ObserveOnSubscriber = (function (_super) { __extends(ObserveOnSubscriber, _super); function ObserveOnSubscriber(destination, scheduler, delay) { if (delay === void 0) { delay = 0; } _super.call(this, destination); this.scheduler = scheduler; this.delay = delay; } ObserveOnSubscriber.dispatch = function (arg) { var notification = arg.notification, destination = arg.destination; notification.observe(destination); this.unsubscribe(); }; ObserveOnSubscriber.prototype.scheduleMessage = function (notification) { this.add(this.scheduler.schedule(ObserveOnSubscriber.dispatch, this.delay, new ObserveOnMessage(notification, this.destination))); }; ObserveOnSubscriber.prototype._next = function (value) { this.scheduleMessage(Notification_1.Notification.createNext(value)); }; ObserveOnSubscriber.prototype._error = function (err) { this.scheduleMessage(Notification_1.Notification.createError(err)); }; ObserveOnSubscriber.prototype._complete = function () { this.scheduleMessage(Notification_1.Notification.createComplete()); }; return ObserveOnSubscriber; }(Subscriber_1.Subscriber)); exports.ObserveOnSubscriber = ObserveOnSubscriber; var ObserveOnMessage = (function () { function ObserveOnMessage(notification, destination) { this.notification = notification; this.destination = destination; } return ObserveOnMessage; }()); exports.ObserveOnMessage = ObserveOnMessage; },{"167":167,"172":172}],215:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /* tslint:disable:max-line-length */ /** * Applies an accumulator function over the source Observable, and returns the * accumulated result when the source completes, given an optional seed value. * * Combines together all values emitted on the source, * using an accumulator function that knows how to join a new source value into * the accumulation from the past. * * * * Like * [Array.prototype.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce), * `reduce` applies an `accumulator` function against an accumulation and each * value of the source Observable (from the past) to reduce it to a single * value, emitted on the output Observable. Note that `reduce` will only emit * one value, only when the source Observable completes. It is equivalent to * applying operator {@link scan} followed by operator {@link last}. * * Returns an Observable that applies a specified `accumulator` function to each * item emitted by the source Observable. If a `seed` value is specified, then * that value will be used as the initial value for the accumulator. If no seed * value is specified, the first item of the source is used as the seed. * * @example Count the number of click events that happened in 5 seconds * var clicksInFiveSeconds = Rx.Observable.fromEvent(document, 'click') * .takeUntil(Rx.Observable.interval(5000)); * var ones = clicksInFiveSeconds.mapTo(1); * var seed = 0; * var count = ones.reduce((acc, one) => acc + one, seed); * count.subscribe(x => console.log(x)); * * @see {@link count} * @see {@link expand} * @see {@link mergeScan} * @see {@link scan} * * @param {function(acc: R, value: T, index: number): R} accumulator The accumulator function * called on each source value. * @param {R} [seed] The initial accumulation value. * @return {Observable} An observable of the accumulated values. * @return {Observable} An Observable that emits a single value that is the * result of accumulating the values emitted by the source Observable. * @method reduce * @owner Observable */ function reduce(accumulator, seed) { var hasSeed = false; // providing a seed of `undefined` *should* be valid and trigger // hasSeed! so don't use `seed !== undefined` checks! // For this reason, we have to check it here at the original call site // otherwise inside Operator/Subscriber we won't know if `undefined` // means they didn't provide anything or if they literally provided `undefined` if (arguments.length >= 2) { hasSeed = true; } return this.lift(new ReduceOperator(accumulator, seed, hasSeed)); } exports.reduce = reduce; var ReduceOperator = (function () { function ReduceOperator(accumulator, seed, hasSeed) { if (hasSeed === void 0) { hasSeed = false; } this.accumulator = accumulator; this.seed = seed; this.hasSeed = hasSeed; } ReduceOperator.prototype.call = function (subscriber, source) { return source.subscribe(new ReduceSubscriber(subscriber, this.accumulator, this.seed, this.hasSeed)); }; return ReduceOperator; }()); exports.ReduceOperator = ReduceOperator; /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var ReduceSubscriber = (function (_super) { __extends(ReduceSubscriber, _super); function ReduceSubscriber(destination, accumulator, seed, hasSeed) { _super.call(this, destination); this.accumulator = accumulator; this.hasSeed = hasSeed; this.index = 0; this.hasValue = false; this.acc = seed; if (!this.hasSeed) { this.index++; } } ReduceSubscriber.prototype._next = function (value) { if (this.hasValue || (this.hasValue = this.hasSeed)) { this._tryReduce(value); } else { this.acc = value; this.hasValue = true; } }; ReduceSubscriber.prototype._tryReduce = function (value) { var result; try { result = this.accumulator(this.acc, value, this.index++); } catch (err) { this.destination.error(err); return; } this.acc = result; }; ReduceSubscriber.prototype._complete = function () { if (this.hasValue || this.hasSeed) { this.destination.next(this.acc); } this.destination.complete(); }; return ReduceSubscriber; }(Subscriber_1.Subscriber)); exports.ReduceSubscriber = ReduceSubscriber; },{"172":172}],216:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscriber_1 = require(172); /** * @return {Observable|WebSocketSubject|Observable} * @method toArray * @owner Observable */ function toArray() { return this.lift(new ToArrayOperator()); } exports.toArray = toArray; var ToArrayOperator = (function () { function ToArrayOperator() { } ToArrayOperator.prototype.call = function (subscriber, source) { return source.subscribe(new ToArraySubscriber(subscriber)); }; return ToArrayOperator; }()); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var ToArraySubscriber = (function (_super) { __extends(ToArraySubscriber, _super); function ToArraySubscriber(destination) { _super.call(this, destination); this.array = []; } ToArraySubscriber.prototype._next = function (x) { this.array.push(x); }; ToArraySubscriber.prototype._complete = function () { this.destination.next(this.array); this.destination.complete(); }; return ToArraySubscriber; }(Subscriber_1.Subscriber)); },{"172":172}],217:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Subscription_1 = require(173); /** * A unit of work to be executed in a {@link Scheduler}. An action is typically * created from within a Scheduler and an RxJS user does not need to concern * themselves about creating and manipulating an Action. * * ```ts * class Action extends Subscription { * new (scheduler: Scheduler, work: (state?: T) => void); * schedule(state?: T, delay: number = 0): Subscription; * } * ``` * * @class Action */ var Action = (function (_super) { __extends(Action, _super); function Action(scheduler, work) { _super.call(this); } /** * Schedules this action on its parent Scheduler for execution. May be passed * some context object, `state`. May happen at some point in the future, * according to the `delay` parameter, if specified. * @param {T} [state] Some contextual data that the `work` function uses when * called by the Scheduler. * @param {number} [delay] Time to wait before executing the work, where the * time unit is implicit and defined by the Scheduler. * @return {void} */ Action.prototype.schedule = function (state, delay) { if (delay === void 0) { delay = 0; } return this; }; return Action; }(Subscription_1.Subscription)); exports.Action = Action; },{"173":173}],218:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var root_1 = require(233); var Action_1 = require(217); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var AsyncAction = (function (_super) { __extends(AsyncAction, _super); function AsyncAction(scheduler, work) { _super.call(this, scheduler, work); this.scheduler = scheduler; this.work = work; this.pending = false; } AsyncAction.prototype.schedule = function (state, delay) { if (delay === void 0) { delay = 0; } if (this.closed) { return this; } // Always replace the current state with the new state. this.state = state; // Set the pending flag indicating that this action has been scheduled, or // has recursively rescheduled itself. this.pending = true; var id = this.id; var scheduler = this.scheduler; // // Important implementation note: // // Actions only execute once by default, unless rescheduled from within the // scheduled callback. This allows us to implement single and repeat // actions via the same code path, without adding API surface area, as well // as mimic traditional recursion but across asynchronous boundaries. // // However, JS runtimes and timers distinguish between intervals achieved by // serial `setTimeout` calls vs. a single `setInterval` call. An interval of // serial `setTimeout` calls can be individually delayed, which delays // scheduling the next `setTimeout`, and so on. `setInterval` attempts to // guarantee the interval callback will be invoked more precisely to the // interval period, regardless of load. // // Therefore, we use `setInterval` to schedule single and repeat actions. // If the action reschedules itself with the same delay, the interval is not // canceled. If the action doesn't reschedule, or reschedules with a // different delay, the interval will be canceled after scheduled callback // execution. // if (id != null) { this.id = this.recycleAsyncId(scheduler, id, delay); } this.delay = delay; // If this action has already an async Id, don't request a new one. this.id = this.id || this.requestAsyncId(scheduler, this.id, delay); return this; }; AsyncAction.prototype.requestAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } return root_1.root.setInterval(scheduler.flush.bind(scheduler, this), delay); }; AsyncAction.prototype.recycleAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } // If this action is rescheduled with the same delay time, don't clear the interval id. if (delay !== null && this.delay === delay) { return id; } // Otherwise, if the action's delay time is different from the current delay, // clear the interval id return root_1.root.clearInterval(id) && undefined || undefined; }; /** * Immediately executes this action and the `work` it contains. * @return {any} */ AsyncAction.prototype.execute = function (state, delay) { if (this.closed) { return new Error('executing a cancelled action'); } this.pending = false; var error = this._execute(state, delay); if (error) { return error; } else if (this.pending === false && this.id != null) { // Dequeue if the action didn't reschedule itself. Don't call // unsubscribe(), because the action could reschedule later. // For example: // ``` // scheduler.schedule(function doWork(counter) { // /* ... I'm a busy worker bee ... */ // var originalAction = this; // /* wait 100ms before rescheduling the action */ // setTimeout(function () { // originalAction.schedule(counter + 1); // }, 100); // }, 1000); // ``` this.id = this.recycleAsyncId(this.scheduler, this.id, null); } }; AsyncAction.prototype._execute = function (state, delay) { var errored = false; var errorValue = undefined; try { this.work(state); } catch (e) { errored = true; errorValue = !!e && e || new Error(e); } if (errored) { this.unsubscribe(); return errorValue; } }; AsyncAction.prototype._unsubscribe = function () { var id = this.id; var scheduler = this.scheduler; var actions = scheduler.actions; var index = actions.indexOf(this); this.work = null; this.delay = null; this.state = null; this.pending = false; this.scheduler = null; if (index !== -1) { actions.splice(index, 1); } if (id != null) { this.id = this.recycleAsyncId(scheduler, id, null); } }; return AsyncAction; }(Action_1.Action)); exports.AsyncAction = AsyncAction; },{"217":217,"233":233}],219:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Scheduler_1 = require(171); var AsyncScheduler = (function (_super) { __extends(AsyncScheduler, _super); function AsyncScheduler() { _super.apply(this, arguments); this.actions = []; /** * A flag to indicate whether the Scheduler is currently executing a batch of * queued actions. * @type {boolean} */ this.active = false; /** * An internal ID used to track the latest asynchronous task such as those * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and * others. * @type {any} */ this.scheduled = undefined; } AsyncScheduler.prototype.flush = function (action) { var actions = this.actions; if (this.active) { actions.push(action); return; } var error; this.active = true; do { if (error = action.execute(action.state, action.delay)) { break; } } while (action = actions.shift()); // exhaust the scheduler queue this.active = false; if (error) { while (action = actions.shift()) { action.unsubscribe(); } throw error; } }; return AsyncScheduler; }(Scheduler_1.Scheduler)); exports.AsyncScheduler = AsyncScheduler; },{"171":171}],220:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var AsyncAction_1 = require(218); /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ var QueueAction = (function (_super) { __extends(QueueAction, _super); function QueueAction(scheduler, work) { _super.call(this, scheduler, work); this.scheduler = scheduler; this.work = work; } QueueAction.prototype.schedule = function (state, delay) { if (delay === void 0) { delay = 0; } if (delay > 0) { return _super.prototype.schedule.call(this, state, delay); } this.delay = delay; this.state = state; this.scheduler.flush(this); return this; }; QueueAction.prototype.execute = function (state, delay) { return (delay > 0 || this.closed) ? _super.prototype.execute.call(this, state, delay) : this._execute(state, delay); }; QueueAction.prototype.requestAsyncId = function (scheduler, id, delay) { if (delay === void 0) { delay = 0; } // If delay exists and is greater than 0, or if the delay is null (the // action wasn't rescheduled) but was originally scheduled as an async // action, then recycle as an async action. if ((delay !== null && delay > 0) || (delay === null && this.delay > 0)) { return _super.prototype.requestAsyncId.call(this, scheduler, id, delay); } // Otherwise flush the scheduler starting with this action. return scheduler.flush(this); }; return QueueAction; }(AsyncAction_1.AsyncAction)); exports.QueueAction = QueueAction; },{"218":218}],221:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var AsyncScheduler_1 = require(219); var QueueScheduler = (function (_super) { __extends(QueueScheduler, _super); function QueueScheduler() { _super.apply(this, arguments); } return QueueScheduler; }(AsyncScheduler_1.AsyncScheduler)); exports.QueueScheduler = QueueScheduler; },{"219":219}],222:[function(require,module,exports){ "use strict"; var QueueAction_1 = require(220); var QueueScheduler_1 = require(221); exports.queue = new QueueScheduler_1.QueueScheduler(QueueAction_1.QueueAction); },{"220":220,"221":221}],223:[function(require,module,exports){ "use strict"; var root_1 = require(233); function symbolIteratorPonyfill(root) { var Symbol = root.Symbol; if (typeof Symbol === 'function') { if (!Symbol.iterator) { Symbol.iterator = Symbol('iterator polyfill'); } return Symbol.iterator; } else { // [for Mozilla Gecko 27-35:](https://mzl.la/2ewE1zC) var Set_1 = root.Set; if (Set_1 && typeof new Set_1()['@@iterator'] === 'function') { return '@@iterator'; } var Map_1 = root.Map; // required for compatability with es6-shim if (Map_1) { var keys = Object.getOwnPropertyNames(Map_1.prototype); for (var i = 0; i < keys.length; ++i) { var key = keys[i]; // according to spec, Map.prototype[@@iterator] and Map.orototype.entries must be equal. if (key !== 'entries' && key !== 'size' && Map_1.prototype[key] === Map_1.prototype['entries']) { return key; } } } return '@@iterator'; } } exports.symbolIteratorPonyfill = symbolIteratorPonyfill; exports.$$iterator = symbolIteratorPonyfill(root_1.root); },{"233":233}],224:[function(require,module,exports){ "use strict"; var root_1 = require(233); function getSymbolObservable(context) { var $$observable; var Symbol = context.Symbol; if (typeof Symbol === 'function') { if (Symbol.observable) { $$observable = Symbol.observable; } else { $$observable = Symbol('observable'); Symbol.observable = $$observable; } } else { $$observable = '@@observable'; } return $$observable; } exports.getSymbolObservable = getSymbolObservable; exports.$$observable = getSymbolObservable(root_1.root); },{"233":233}],225:[function(require,module,exports){ "use strict"; var root_1 = require(233); var Symbol = root_1.root.Symbol; exports.$$rxSubscriber = (typeof Symbol === 'function' && typeof Symbol.for === 'function') ? Symbol.for('rxSubscriber') : '@@rxSubscriber'; },{"233":233}],226:[function(require,module,exports){ "use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; /** * An error thrown when one or more errors have occurred during the * `unsubscribe` of a {@link Subscription}. */ var UnsubscriptionError = (function (_super) { __extends(UnsubscriptionError, _super); function UnsubscriptionError(errors) { _super.call(this); this.errors = errors; var err = Error.call(this, errors ? errors.length + " errors occurred during unsubscription:\n " + errors.map(function (err, i) { return ((i + 1) + ") " + err.toString()); }).join('\n ') : ''); this.name = err.name = 'UnsubscriptionError'; this.stack = err.stack; this.message = err.message; } return UnsubscriptionError; }(Error)); exports.UnsubscriptionError = UnsubscriptionError; },{}],227:[function(require,module,exports){ "use strict"; // typeof any so that it we don't have to cast when comparing a result to the error object exports.errorObject = { e: {} }; },{}],228:[function(require,module,exports){ "use strict"; exports.isArray = Array.isArray || (function (x) { return x && typeof x.length === 'number'; }); },{}],229:[function(require,module,exports){ "use strict"; function isFunction(x) { return typeof x === 'function'; } exports.isFunction = isFunction; },{}],230:[function(require,module,exports){ "use strict"; function isObject(x) { return x != null && typeof x === 'object'; } exports.isObject = isObject; },{}],231:[function(require,module,exports){ "use strict"; function isPromise(value) { return value && typeof value.subscribe !== 'function' && typeof value.then === 'function'; } exports.isPromise = isPromise; },{}],232:[function(require,module,exports){ "use strict"; function isScheduler(value) { return value && typeof value.schedule === 'function'; } exports.isScheduler = isScheduler; },{}],233:[function(require,module,exports){ (function (global){(function (){ "use strict"; /** * window: browser in DOM main thread * self: browser in WebWorker * global: Node.js/other */ exports.root = (typeof window == 'object' && window.window === window && window || typeof self == 'object' && self.self === self && self || typeof global == 'object' && global.global === global && global); if (!exports.root) { throw new Error('RxJS could not find any global context (window, self, global)'); } }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],234:[function(require,module,exports){ "use strict"; var root_1 = require(233); var isArray_1 = require(228); var isPromise_1 = require(231); var isObject_1 = require(230); var Observable_1 = require(168); var iterator_1 = require(223); var InnerSubscriber_1 = require(166); var observable_1 = require(224); function subscribeToResult(outerSubscriber, result, outerValue, outerIndex) { var destination = new InnerSubscriber_1.InnerSubscriber(outerSubscriber, outerValue, outerIndex); if (destination.closed) { return null; } if (result instanceof Observable_1.Observable) { if (result._isScalar) { destination.next(result.value); destination.complete(); return null; } else { return result.subscribe(destination); } } else if (isArray_1.isArray(result)) { for (var i = 0, len = result.length; i < len && !destination.closed; i++) { destination.next(result[i]); } if (!destination.closed) { destination.complete(); } } else if (isPromise_1.isPromise(result)) { result.then(function (value) { if (!destination.closed) { destination.next(value); destination.complete(); } }, function (err) { return destination.error(err); }) .then(null, function (err) { // Escaping the Promise trap: globally throw unhandled errors root_1.root.setTimeout(function () { throw err; }); }); return destination; } else if (result && typeof result[iterator_1.$$iterator] === 'function') { var iterator = result[iterator_1.$$iterator](); do { var item = iterator.next(); if (item.done) { destination.complete(); break; } destination.next(item.value); if (destination.closed) { break; } } while (true); } else if (result && typeof result[observable_1.$$observable] === 'function') { var obs = result[observable_1.$$observable](); if (typeof obs.subscribe !== 'function') { destination.error(new TypeError('Provided object does not correctly implement Symbol.observable')); } else { return obs.subscribe(new InnerSubscriber_1.InnerSubscriber(outerSubscriber, outerValue, outerIndex)); } } else { var value = isObject_1.isObject(result) ? 'an invalid object' : "'" + result + "'"; var msg = ("You provided " + value + " where a stream was expected.") + ' You can provide an Observable, Promise, Array, or Iterable.'; destination.error(new TypeError(msg)); } return null; } exports.subscribeToResult = subscribeToResult; },{"166":166,"168":168,"223":223,"224":224,"228":228,"230":230,"231":231,"233":233}],235:[function(require,module,exports){ "use strict"; var Subscriber_1 = require(172); var rxSubscriber_1 = require(225); var Observer_1 = require(169); function toSubscriber(nextOrObserver, error, complete) { if (nextOrObserver) { if (nextOrObserver instanceof Subscriber_1.Subscriber) { return nextOrObserver; } if (nextOrObserver[rxSubscriber_1.$$rxSubscriber]) { return nextOrObserver[rxSubscriber_1.$$rxSubscriber](); } } if (!nextOrObserver && !error && !complete) { return new Subscriber_1.Subscriber(Observer_1.empty); } return new Subscriber_1.Subscriber(nextOrObserver, error, complete); } exports.toSubscriber = toSubscriber; },{"169":169,"172":172,"225":225}],236:[function(require,module,exports){ "use strict"; var errorObject_1 = require(227); var tryCatchTarget; function tryCatcher() { try { return tryCatchTarget.apply(this, arguments); } catch (e) { errorObject_1.errorObject.e = e; return errorObject_1.errorObject; } } function tryCatch(fn) { tryCatchTarget = fn; return tryCatcher; } exports.tryCatch = tryCatch; ; },{"227":227}],237:[function(require,module,exports){ var prefix = require(297); var Keys = { ranges: prefix + 'ranges', integers: prefix + 'integers', keys: prefix + 'keys', named: prefix + 'named', name: prefix + 'name', match: prefix + 'match' }; module.exports = Keys; },{"297":297}],238:[function(require,module,exports){ var Precedence = { specific: 4, ranges: 2, integers: 2, keys: 1 }; module.exports = Precedence; },{}],239:[function(require,module,exports){ var Keys = require(237); var parseTree = require(271); var matcher = require(254); var JSONGraphError = require(248); var MAX_REF_FOLLOW = 50; var MAX_PATHS = 9000; var noOp = function noOp() {}; var defaultNow = function defaultNow() { return Date.now(); }; var Router = function(routes, options) { this._routes = routes; this._rst = parseTree(routes); this._matcher = matcher(this._rst); this._setOptions(options); }; Router.createClass = function(routes) { function C(options) { this._setOptions(options); } C.prototype = new Router(routes); C.prototype.constructor = C; return C; }; Router.prototype = { /** * Performs the get algorithm on the router. * @param {PathSet[]} paths - * @returns {Observable.} */ get: require(273), /** * Takes in a jsonGraph and outputs a Observable.. The set * method will use get until it evaluates the last key of the path inside * of paths. At that point it will produce an intermediate structure that * matches the path and has the value that is found in the jsonGraph env. * * One of the requirements for interaction with a dataSource is that the * set message must be optimized to the best of the incoming sources * knowledge. * * @param {JSONGraphEnvelope} jsonGraph - * @returns {Observable.} */ set: require(275), /** * Invokes a function in the DataSource's JSONGraph object at the path * provided in the callPath argument. If there are references that are * followed, a get will be performed to get to the call function. * * @param {Path} callPath - * @param {Array.<*>} args - * @param {Array.} refPaths - * @param {Array.} thisPaths - */ call: require(272), /** * When a route misses on a call, get, or set the unhandledDataSource will * have a chance to fulfill that request. * @param {DataSource} dataSource - */ routeUnhandledPathsTo: function routeUnhandledPathsTo(dataSource) { this._unhandled = dataSource; }, _setOptions: function _setOptions(options) { var opts = options || {}; this._debug = opts.debug; this._pathErrorHook = (opts.hooks && opts.hooks.pathError) || noOp; this._errorHook = opts.hooks && opts.hooks.error; this._methodSummaryHook = opts.hooks && opts.hooks.methodSummary; this._now = (opts.hooks && opts.hooks.now) || opts.now || defaultNow; this.maxRefFollow = opts.maxRefFollow || MAX_REF_FOLLOW; this.maxPaths = opts.maxPaths || MAX_PATHS; } }; Router.ranges = Keys.ranges; Router.integers = Keys.integers; Router.keys = Keys.keys; Router.JSONGraphError = JSONGraphError; module.exports = Router; },{"237":237,"248":248,"254":254,"271":271,"272":272,"273":273,"275":275}],240:[function(require,module,exports){ var RouterRx = { Observable: require(168).Observable, Scheduler: { queue: require(222).queue } }; require(174); require(177); require(176); require(178); require(175); require(187); require(182); require(181); require(186); require(183); require(188); require(189); require(185); require(184); require(179); require(180); module.exports = RouterRx; },{"168":168,"174":174,"175":175,"176":176,"177":177,"178":178,"179":179,"180":180,"181":181,"182":182,"183":183,"184":184,"185":185,"186":186,"187":187,"188":188,"189":189,"222":222}],241:[function(require,module,exports){ var cloneArray = require(290); var $ref = require(301).$ref; var errors = require(250); /** * performs the simplified cache reference follow. This * differs from get as there is just following and reporting, * not much else. * * @param {Object} cacheRoot * @param {Array} ref */ module.exports = function followReference(cacheRoot, ref, maxRefFollow) { var current = cacheRoot; var refPath = ref; var depth = -1; var length = refPath.length; var key, next, type; var referenceCount = 0; while (++depth < length) { key = refPath[depth]; next = current[key]; type = next && next.$type; if (!next || type && type !== $ref) { current = next; break; } // Show stopper exception. This route is malformed. if (type && type === $ref && depth + 1 < length) { var err = new Error(errors.innerReferences); err.throwToNext = true; throw err; } // potentially follow reference if (depth + 1 === length) { if (type === $ref) { depth = -1; refPath = next.value; length = refPath.length; next = cacheRoot; referenceCount++; } if (referenceCount > maxRefFollow) { throw new Error(errors.circularReference); } } current = next; } return [current, cloneArray(refPath)]; }; },{"250":250,"290":290,"301":301}],242:[function(require,module,exports){ /** * To simplify this algorithm, the path must be a simple * path with no complex keys. * * Note: The path coming in must contain no references, as * all set data caches have no references. * @param {Object} cache * @param {PathSet} path */ module.exports = function getValue(cache, path) { return path.reduce(function(acc, key) { return acc[key]; }, cache); }; },{}],243:[function(require,module,exports){ var iterateKeySet = require(155).iterateKeySet; var types = require(301); var $ref = types.$ref; var $error = types.$error; var clone = require(289); var cloneArray = require(290); var catAndSlice = require(288); /** * merges jsong into a seed */ module.exports = function jsongMerge(cache, jsongEnv, routerInstance) { var paths = jsongEnv.paths; var j = jsongEnv.jsonGraph; var references = []; var values = []; paths.forEach(function(p) { merge({ router: routerInstance, cacheRoot: cache, messageRoot: j, references: references, values: values, requestedPath: [], requestIdx: -1, ignoreCount: 0 }, cache, j, 0, p); }); return { references: references, values: values }; }; function merge(config, cache, message, depth, path, fromParent, fromKey) { var cacheRoot = config.cacheRoot; var messageRoot = config.messageRoot; var requestedPath = config.requestedPath; var ignoreCount = config.ignoreCount; var typeOfMessage = typeof message; var requestIdx = config.requestIdx; var updateRequestedPath = ignoreCount <= depth; if (updateRequestedPath) { requestIdx = ++config.requestIdx; } // The message at this point should always be defined. // Reached the end of the JSONG message path if (message === null || typeOfMessage !== 'object' || message.$type) { fromParent[fromKey] = clone(message); // If we notice an error while merging, we'll fire the error hook // for logging purposes. if (message && message.$type === $error) { config.router._pathErrorHook({ path: path, value: message }); } // NOTE: If we have found a reference at our cloning position // and we have resolved our path then add the reference to // the unfulfilledRefernces. if (message && message.$type === $ref) { var references = config.references; references.push({ path: cloneArray(requestedPath), value: message.value }); } // We are dealing with a value. We need this for call // Call needs to report all of its values into the jsongCache // and paths. else { var values = config.values; values.push({ path: cloneArray(requestedPath), value: (message && message.type) ? message.value : message }); } return; } var outerKey = path[depth]; var iteratorNote = {}; var key; key = iterateKeySet(outerKey, iteratorNote); // We always attempt this as a loop. If the memo exists then // we assume that the permutation is needed. do { // If the cache exists and we are not at our height, then // just follow cache, else attempt to follow message. var cacheRes = cache[key]; var messageRes = message[key]; // We no longer materialize inside of jsonGraph merge. Either the // client should specify all of the paths if (messageRes !== undefined) { var nextPath = path; var nextDepth = depth + 1; if (updateRequestedPath) { requestedPath[requestIdx] = key; } // We do not continue with this branch since the cache if (cacheRes === undefined) { cacheRes = cache[key] = {}; } var nextIgnoreCount = ignoreCount; // TODO: Potential performance gain since we know that // references are always pathSets of 1, they can be evaluated // iteratively. // There is only a need to consider message references since the // merge is only for the path that is provided. if (messageRes && messageRes.$type === $ref && depth < path.length - 1) { nextDepth = 0; nextPath = catAndSlice(messageRes.value, path, depth + 1); cache[key] = clone(messageRes); // Reset position in message and cache. nextIgnoreCount = messageRes.value.length; messageRes = messageRoot; cacheRes = cacheRoot; } // move forward down the path progression. config.ignoreCount = nextIgnoreCount; merge(config, cacheRes, messageRes, nextDepth, nextPath, cache, key); config.ignoreCount = ignoreCount; } if (updateRequestedPath) { requestedPath.length = requestIdx; } // Are we done with the loop? key = iterateKeySet(outerKey, iteratorNote); } while (!iteratorNote.done); } },{"155":155,"288":288,"289":289,"290":290,"301":301}],244:[function(require,module,exports){ var iterateKeySet = require(155).iterateKeySet; var cloneArray = require(290); var catAndSlice = require(288); var $types = require(301); var $ref = $types.$ref; var followReference = require(241); /** * The fastest possible optimize of paths. * * What it does: * - Any atom short-circuit / found value will be removed from the path. * - All paths will be exploded which means that collapse will need to be * ran afterwords. * - Any missing path will be optimized as much as possible. */ module.exports = function optimizePathSets(cache, paths, maxRefFollow) { var optimized = []; paths.forEach(function(p) { optimizePathSet(cache, cache, p, 0, optimized, [], maxRefFollow); }); return optimized; }; /** * optimizes one pathSet at a time. */ function optimizePathSet(cache, cacheRoot, pathSet, depth, out, optimizedPath, maxRefFollow) { // at missing, report optimized path. if (cache === undefined) { out[out.length] = catAndSlice(optimizedPath, pathSet, depth); return; } // all other sentinels are short circuited. // Or we found a primitive (which includes null) if (cache === null || (cache.$type && cache.$type !== $ref) || (typeof cache !== 'object')) { return; } // If the reference is the last item in the path then do not // continue to search it. if (cache.$type === $ref && depth === pathSet.length) { return; } var keySet = pathSet[depth]; var nextDepth = depth + 1; var iteratorNote = {}; var key, next, nextOptimized; key = iterateKeySet(keySet, iteratorNote); do { next = cache[key]; var optimizedPathLength = optimizedPath.length; if (key !== null) { optimizedPath[optimizedPathLength] = key; } if (next && next.$type === $ref && nextDepth < pathSet.length) { var refResults = followReference(cacheRoot, next.value, maxRefFollow); next = refResults[0]; // must clone to avoid the mutation from above destroying the cache. nextOptimized = cloneArray(refResults[1]); } else { nextOptimized = optimizedPath; } optimizePathSet(next, cacheRoot, pathSet, nextDepth, out, nextOptimized, maxRefFollow); optimizedPath.length = optimizedPathLength; if (!iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (!iteratorNote.done); } },{"155":155,"241":241,"288":288,"290":290,"301":301}],245:[function(require,module,exports){ var clone = require(289); var types = require(301); var $ref = types.$ref; var iterateKeySet = require(155).iterateKeySet; /** * merges pathValue into a cache */ module.exports = function pathValueMerge(cache, pathValue) { var refs = []; var values = []; var invalidations = []; var valueType = true; // The pathValue invalidation shape. if (pathValue.invalidated === true) { invalidations.push({path: pathValue.path}); valueType = false; } // References. Needed for evaluationg suffixes in all three types, get, // call and set. else if ((pathValue.value !== null) && (pathValue.value.$type === $ref)) { refs.push({ path: pathValue.path, value: pathValue.value.value }); } // Values. Needed for reporting for call. else { values.push(pathValue); } // If the type of pathValue is a valueType (reference or value) then merge // it into the jsonGraph cache. if (valueType) { innerPathValueMerge(cache, pathValue); } return { references: refs, values: values, invalidations: invalidations }; }; function innerPathValueMerge(cache, pathValue) { var path = pathValue.path; var curr = cache; var next, key, cloned, outerKey, iteratorNote; var i, len; for (i = 0, len = path.length - 1; i < len; ++i) { outerKey = path[i]; // Setup the memo and the key. if (outerKey && typeof outerKey === 'object') { iteratorNote = {}; key = iterateKeySet(outerKey, iteratorNote); } else { key = outerKey; iteratorNote = false; } do { next = curr[key]; if (!next) { next = curr[key] = {}; } if (iteratorNote) { innerPathValueMerge( next, { path: path.slice(i + 1), value: pathValue.value }); if (!iteratorNote.done) { key = iterateKeySet(outerKey, iteratorNote); } } else { curr = next; } } while (iteratorNote && !iteratorNote.done); // All memoized paths need to be stopped to avoid // extra key insertions. if (iteratorNote) { return; } } // TODO: This clearly needs a re-write. I am just unsure of how i want // this to look. Plus i want to measure performance. outerKey = path[i]; iteratorNote = {}; key = iterateKeySet(outerKey, iteratorNote); do { cloned = clone(pathValue.value); curr[key] = cloned; if (!iteratorNote.done) { key = iterateKeySet(outerKey, iteratorNote); } } while (!iteratorNote.done); } },{"155":155,"289":289,"301":301}],246:[function(require,module,exports){ var MESSAGE = 'function does not exist.'; var CallNotFoundError = module.exports = function CallNotFoundError() { this.message = MESSAGE; this.stack = (new Error()).stack; }; CallNotFoundError.prototype = new Error(); },{}],247:[function(require,module,exports){ var MESSAGE = 'Any JSONG-Graph returned from call must have paths.'; var CallRequiresPathsError = function CallRequiresPathsError() { this.message = MESSAGE; this.stack = (new Error()).stack; }; CallRequiresPathsError.prototype = new Error(); module.exports = CallRequiresPathsError; },{}],248:[function(require,module,exports){ var JSONGraphError = module.exports = function JSONGraphError(typeValue) { this.typeValue = typeValue; }; JSONGraphError.prototype = new Error(); },{}],249:[function(require,module,exports){ var MESSAGE = "Maximum number of paths exceeded."; var MaxPathsExceededError = function MaxPathsExceededError(message) { this.message = message === undefined ? MESSAGE : message; this.stack = (new Error()).stack; }; MaxPathsExceededError.prototype = new Error(); MaxPathsExceededError.prototype.throwToNext = true; module.exports = MaxPathsExceededError; },{}],250:[function(require,module,exports){ /*eslint-disable*/ module.exports = { innerReferences: 'References with inner references are not allowed.', unknown: 'Unknown Error', routeWithSamePrecedence: 'Two routes cannot have the same precedence or path.', circularReference: 'There appears to be a circular reference, maximum reference following exceeded.' }; },{}],251:[function(require,module,exports){ var isArray = Array.isArray; module.exports = function convertPathKeyTo(onRange, onKey) { return function converter(keySet) { var isKeySet = typeof keySet === 'object'; var out = []; // The keySet we determine what type is this keyset. if (isKeySet) { if (isArray(keySet)) { var reducer = null; keySet.forEach(function(key) { if (typeof key === 'object') { reducer = onRange(out, key, reducer); } else { reducer = onKey(out, key, reducer); } }); } // What passed in is a range. else { onRange(out, keySet); } } // simple value for keyset. else { onKey(out, keySet); } return out; }; }; },{}],252:[function(require,module,exports){ var convertPathKeyTo = require(251); var isNumber = require(293); var rangeToArray = require(263); function onRange(out, range) { var len = out.length - 1; rangeToArray(range).forEach(function(el) { out[++len] = el; }); } function onKey(out, key) { if (isNumber(key)) { out[out.length] = key; } } /** * will attempt to get integers from the key * or keySet provided. assumes everything passed in is an integer * or range of integers. */ module.exports = convertPathKeyTo(onRange, onKey); },{"251":251,"263":263,"293":293}],253:[function(require,module,exports){ var convertPathKeyTo = require(251); var rangeToArray = require(263); function onKey(out, key) { out[out.length] = key; } function onRange(out, range) { var len = out.length - 1; rangeToArray(range).forEach(function(el) { out[++len] = el; }); } /** * will attempt to get integers from the key * or keySet provided. assumes everything passed in is an integer * or range of integers. */ module.exports = convertPathKeyTo(onRange, onKey); },{"251":251,"263":263}],254:[function(require,module,exports){ var Keys = require(237); var Precedence = require(238); var cloneArray = require(290); var specificMatcher = require(259); var pluckIntegers = require(258); var pathUtils = require(155); var collapse = pathUtils.collapse; var isRoutedToken = require(296); var CallNotFoundError = require(246); var intTypes = [{ type: Keys.ranges, precedence: Precedence.ranges }, { type: Keys.integers, precedence: Precedence.integers }]; var keyTypes = [{ type: Keys.keys, precedence: Precedence.keys }]; var allTypes = intTypes.concat(keyTypes); var get = 'get'; var set = 'set'; var call = 'call'; /** * Creates a custom matching function for the match tree. * @param Object rst The routed syntax tree * @param String method the method to call at the end of the path. * @return {matched: Array., missingPaths: Array.} */ module.exports = function matcher(rst) { /** * This is where the matching is done. Will recursively * match the paths until it has found all the matchable * functions. * @param {[]} paths */ return function innerMatcher(method, paths) { var matched = []; var missing = []; match(rst, paths, method, matched, missing); // We are at the end of the path but there is no match and its a // call. Therefore we are going to throw an informative error. if (method === call && matched.length === 0) { var err = new CallNotFoundError(); err.throwToNext = true; throw err; } // Reduce into groups multiple matched routes into route sets where // each match matches the same route endpoint. From here we can reduce // the matched paths into the most optimal pathSet with collapse. var reducedMatched = matched.reduce(function(acc, matchedRoute) { if (!acc[matchedRoute.id]) { acc[matchedRoute.id] = []; } acc[matchedRoute.id].push(matchedRoute); return acc; }, {}); var collapsedMatched = []; // For every set of matched routes, collapse and reduce its matched set // down to the minimal amount of collapsed sets. Object. keys(reducedMatched). forEach(function(k) { var reducedMatch = reducedMatched[k]; // If the reduced match is of length one then there is no // need to perform collapsing, as there is nothing to collapse // over. if (reducedMatch.length === 1) { collapsedMatched.push(reducedMatch[0]); return; } // Since there are more than 1 routes, we need to see if // they can collapse and alter the amount of arrays. var collapsedResults = collapse( reducedMatch. map(function(x) { return x.requested; })); // For every collapsed result we use the previously match result // and update its requested and virtual path. Then add that // match to the collapsedMatched set. collapsedResults.forEach(function(path, i) { var collapsedMatch = reducedMatch[i]; var reducedVirtualPath = collapsedMatch.virtual; path.forEach(function(atom, index) { // If its not a routed atom then wholesale replace if (!isRoutedToken(reducedVirtualPath[index])) { reducedVirtualPath[index] = atom; } }); collapsedMatch.requested = path; collapsedMatched.push(reducedMatch[i]); }); }); return collapsedMatched; }; }; function match( curr, path, method, matchedFunctions, missingPaths, depth, requested, virtual, precedence) { // Nothing left to match if (!curr) { return; } /* eslint-disable no-param-reassign */ depth = depth || 0; requested = requested || []; virtual = virtual || []; precedence = precedence || []; matchedFunctions = matchedFunctions || []; /* eslint-disable no-param-reassign */ // At this point in the traversal we have hit a matching function. // Its time to terminate. // Get: simple method matching // Set/Call: The method is unique. If the path is not complete, // meaning the depth is equivalent to the length, // then we match a 'get' method, else we match a 'set' or 'call' method. var atEndOfPath = path.length === depth; var isSet = method === set; var isCall = method === call; var methodToUse = method; if ((isCall || isSet) && !atEndOfPath) { methodToUse = get; } // Stores the matched result if found along or at the end of // the path. If we are doing a set and there is no set handler // but there is a get handler, then we need to use the get // handler. This is so that the current value that is in the // clients cache does not get materialized away. var currentMatch = curr[Keys.match]; // From https://github.com/Netflix/falcor-router/issues/76 // Set: When there is no set hander then we should default to running // the get handler so that we do not destroy the client local values. if (currentMatch && isSet && !currentMatch[set]) { methodToUse = get; } // Check to see if we have if (currentMatch && currentMatch[methodToUse]) { matchedFunctions[matchedFunctions.length] = { // Used for collapsing paths that use routes with multiple // string indexers. id: currentMatch[methodToUse + 'Id'], requested: cloneArray(requested), prettyRoute: currentMatch.prettyRoute, action: currentMatch[methodToUse], authorize: currentMatch.authorize, virtual: cloneArray(virtual), precedence: +(precedence.join('')), suffix: path.slice(depth), isSet: atEndOfPath && isSet, isCall: atEndOfPath && isCall }; } // If the depth has reached the end then we need to stop recursing. This // can cause odd side effects with matching against {keys} as the last // argument when a path has been exhausted (undefined is still a key value). // // Example: // route1: [{keys}] // route2: [{keys}][{keys}] // // path: ['(']. // // This will match route1 and 2 since we do not bail out on length and there // is a {keys} matcher which will match "undefined" value. if (depth === path.length) { return; } var keySet = path[depth]; var i, len, key, next; // ------------------------------------------- // Specific key matcher. // ------------------------------------------- var specificKeys = specificMatcher(keySet, curr); for (i = 0, len = specificKeys.length; i < len; ++i) { key = specificKeys[i]; virtual[depth] = key; requested[depth] = key; precedence[depth] = Precedence.specific; // Its time to recurse match( curr[specificKeys[i]], path, method, matchedFunctions, missingPaths, depth + 1, requested, virtual, precedence); // Removes the virtual, requested, and precedence info virtual.length = depth; requested.length = depth; precedence.length = depth; } var ints = pluckIntegers(keySet); var keys = keySet; var intsLength = ints.length; // ------------------------------------------- // ints, ranges, and keys matcher. // ------------------------------------------- allTypes. filter(function(typeAndPrecedence) { var type = typeAndPrecedence.type; // one extra move required for int types if (type === Keys.integers || type === Keys.ranges) { return curr[type] && intsLength; } return curr[type]; }). forEach(function(typeAndPrecedence) { var type = typeAndPrecedence.type; var prec = typeAndPrecedence.precedence; next = curr[type]; virtual[depth] = { type: type, named: next[Keys.named], name: next[Keys.name] }; // The requested set of info needs to be set either // as ints, if int matchers or keys if (type === Keys.integers || type === Keys.ranges) { requested[depth] = ints; } else { requested[depth] = keys; } precedence[depth] = prec; // Continue the matching algo. match( next, path, method, matchedFunctions, missingPaths, depth + 1, requested, virtual, precedence); // removes the added keys virtual.length = depth; requested.length = depth; precedence.length = depth; }); } },{"155":155,"237":237,"238":238,"246":246,"258":258,"259":259,"290":290,"296":296}],255:[function(require,module,exports){ var Keys = require(237); var isArray = Array.isArray; var isRoutedToken = require(296); var isRange = require(295); /** * Takes a matched and virtual atom and validates that they have an * intersection. */ module.exports = function hasAtomIntersection(matchedAtom, virtualAtom) { var virtualIsRoutedToken = isRoutedToken(virtualAtom); var isKeys = virtualIsRoutedToken && virtualAtom.type === Keys.keys; var matched = false; var i, len; // To simplify the algorithm we do not allow matched atom to be an // array. This makes the intersection test very simple. if (isArray(matchedAtom)) { for (i = 0, len = matchedAtom.length; i < len && !matched; ++i) { matched = hasAtomIntersection(matchedAtom[i], virtualAtom); } } // the == is very intentional here with all the use cases review. else if (doubleEquals(matchedAtom, virtualAtom)) { matched = true; } // Keys match everything. else if (isKeys) { matched = true; } // The routed token is for integers at this point. else if (virtualIsRoutedToken) { matched = isNumber(matchedAtom) || isRange(matchedAtom); } // is virtual an array (last option) // Go through each of the array elements and compare against matched item. else if (isArray(virtualAtom)) { for (i = 0, len = virtualAtom.length; i < len && !matched; ++i) { matched = hasAtomIntersection(matchedAtom, virtualAtom[i]); } } return matched; }; // function isNumber(x) { return String(Number(x)) === String(x); } /** * This was very intentional ==. The reason is that '1' must equal 1. * {} of anysort are always false and array ['one'] == 'one' but that is * fine because i would have to go through the array anyways at the * last elseif check. */ function doubleEquals(a, b) { return a == b; // eslint-disable-line eqeqeq } },{"237":237,"295":295,"296":296}],256:[function(require,module,exports){ var hasAtomIntersection = require(255); /** * Checks to see if there is an intersection between the matched and * virtual paths. */ module.exports = function hasIntersection(matchedPath, virtualPath) { var intersection = true; // cycles through the atoms and ensure each one has an intersection. // only use the virtual path because it can be shorter than the full // matched path (since it includes suffix). for (var i = 0, len = virtualPath.length; i < len && intersection; ++i) { intersection = hasAtomIntersection(matchedPath[i], virtualPath[i]); } return intersection; }; },{"255":255}],257:[function(require,module,exports){ /** * @param {PathSet} path - A simple path * @param {Object} tree - The tree should have `null` leaves to denote a * leaf node. */ module.exports = function hasIntersectionWithTree(path, tree) { return _hasIntersection(path, tree, 0); }; function _hasIntersection(path, node, depth) { // Exit / base condition. We have reached the // length of our path and we are at a node of null. if (depth === path.length && node === null) { return true; } var key = path[depth]; var next = node[key]; // If its not undefined, then its a branch. if (node !== undefined) { return _hasIntersection(path, next, depth + 1); } return false; } },{}],258:[function(require,module,exports){ var isArray = Array.isArray; /** * plucks any integers from the path key. Makes no effort * to convert the key into any specific format. */ module.exports = function pluckIntegers(keySet) { var ints = []; if (typeof keySet === 'object') { if (isArray(keySet)) { keySet.forEach(function(key) { // Range case if (typeof key === 'object') { ints[ints.length] = key; } else if (!isNaN(+key)) { ints[ints.length] = +key; } }); } // Range case else { ints[ints.length] = keySet; } } else if (!isNaN(+keySet)) { ints[ints.length] = +keySet; } return ints; }; },{}],259:[function(require,module,exports){ var iterateKeySet = require(155).iterateKeySet; module.exports = function specificMatcher(keySet, currentNode) { // -------------------------------------- // Specific key // -------------------------------------- var iteratorNote = {}; var nexts = []; var key = iterateKeySet(keySet, iteratorNote); do { if (currentNode[key]) { nexts[nexts.length] = key; } if (!iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (!iteratorNote.done); return nexts; }; },{"155":155}],260:[function(require,module,exports){ var convertPathKeyTo = require(251); var isNumber = require(293); function onRange(out, range) { out[out.length] = range; } /** * @param {Number|String} key must be a number */ function keyReduce(out, key, range) { if (!isNumber(key)) { return range; } /* eslint-disable no-param-reassign */ key = +key; if (range) { if (key - 1 === range.to) { range.to = key; } else if (key + 1 === range.from) { range.from = key; } else { range = null; } } if (!range) { range = {to: key, from: key}; out[out.length] = range; } /* eslint-enable no-param-reassign */ return range; } module.exports = convertPathKeyTo(onRange, keyReduce); },{"251":251,"293":293}],261:[function(require,module,exports){ /** * takes in a range and normalizes it to have a to / from */ module.exports = function normalize(range) { var from = range.from || 0; var to; if (typeof range.to === 'number') { to = range.to; } else { to = from + range.length - 1; } return {to: to, from: from}; }; },{}],262:[function(require,module,exports){ var normalize = require(261); /** * warning: This mutates the array of arrays. * It only converts the ranges to properly normalized ranges * so the rest of the algos do not have to consider it. */ module.exports = function normalizePathSets(path) { path.forEach(function(key, i) { // the algo becomes very simple if done recursively. If // speed is needed, this is an easy optimization to make. if (Array.isArray(key)) { normalizePathSets(key); } else if (typeof key === 'object') { path[i] = normalize(path[i]); } }); return path; }; },{"261":261}],263:[function(require,module,exports){ module.exports = function onRange(range) { var out = []; var i = range.from; var to = range.to; var outIdx = out.length; for (; i <= to; ++i, ++outIdx) { out[outIdx] = i; } return out; }; },{}],264:[function(require,module,exports){ var isArray = Array.isArray; var stripFromArray = require(265); var stripFromRange = require(266); /** * Takes a virtual atom and the matched atom and returns an * array of results that is relative complement with matchedAtom * as the rhs. I believe the proper set syntax is virutalAtom \ matchedAtom. * * 1) An assumption made is that the matched atom and virtual atom have * an intersection. This makes the algorithm easier since if the matched * atom is a primitive and the virtual atom is an object * then there is no relative complement to create. This also means if * the direct equality test fails and matchedAtom is not an object * then virtualAtom is an object. The inverse applies. * * * @param {String|Number|Array|Object} matchedAtom * @param {String|Number|Array|Object} virtualAtom * @return {Array} the tuple of what was matched and the relative complenment. */ module.exports = function strip(matchedAtom, virtualAtom) { var relativeComplement = []; var matchedResults; var typeOfMatched = typeof matchedAtom; var isArrayMatched = isArray(matchedAtom); var isObjectMatched = typeOfMatched === 'object'; // Lets assume they are not objects This covers the // string / number cases. if (matchedAtom === virtualAtom || String(matchedAtom) === String(virtualAtom)) { matchedResults = [matchedAtom]; } // See function comment 1) else if (!isObjectMatched) { matchedResults = [matchedAtom]; } // Its a complex object set potentially. Let the // subroutines handle the cases. else { var results; // The matchedAtom needs to reduced to everything that is not in // the virtualAtom. if (isArrayMatched) { results = stripFromArray(virtualAtom, matchedAtom); matchedResults = results[0]; relativeComplement = results[1]; } else { results = stripFromRange(virtualAtom, matchedAtom); matchedResults = results[0]; relativeComplement = results[1]; } } if (matchedResults.length === 1) { matchedResults = matchedResults[0]; } return [matchedResults, relativeComplement]; }; },{"265":265,"266":266}],265:[function(require,module,exports){ var stripFromRange = require(266); var Keys = require(237); var isArray = Array.isArray; /** * Takes a string, number, or RoutedToken and removes it from * the array. The results are the relative complement of what * remains in the array. * * Don't forget: There was an intersection test performed but * since we recurse over arrays, we will get elements that do * not intersect. * * Another one is if its a routed token and a ranged array then * no work needs to be done as integers, ranges, and keys match * that token set. * * One more note. When toStrip is an array, we simply recurse * over each key. Else it requires a lot more logic. * * @param {Array|String|Number|RoutedToken} toStrip * @param {Array} array * @return {Array} the relative complement. */ module.exports = function stripFromArray(toStrip, array) { var complement; var matches = []; var typeToStrip = typeof toStrip; var isRangedArray = typeof array[0] === 'object'; var isNumber = typeToStrip === 'number'; var isString = typeToStrip === 'string'; var isRoutedToken = !isNumber && !isString; var routeType = isRoutedToken && toStrip.type || false; var isKeys = routeType === Keys.keys; var toStripIsArray = isArray(toStrip); // The early break case. If its a key, then there is never a // relative complement. if (isKeys) { complement = []; matches = array; } // Recurse over all the keys of the array. else if (toStripIsArray) { var currentArray = array; toStrip.forEach(function(atom) { var results = stripFromArray(atom, currentArray); if (results[0] !== undefined) { // eslint-disable-line no-undefined matches = matches.concat(results[0]); } currentArray = results[1]; }); complement = currentArray; } // The simple case, remove only the matching element from array. else if (!isRangedArray && !isRoutedToken) { matches = [toStrip]; complement = array.filter(function(x) { return toStrip !== x; }); } // 1: from comments above else if (isRangedArray && !isRoutedToken) { complement = array.reduce(function(comp, range) { var results = stripFromRange(toStrip, range); if (results[0] !== undefined) { // eslint-disable-line no-undefined matches = matches.concat(results[0]); } return comp.concat(results[1]); }, []); } // Strips elements based on routed token type. // We already matched keys above, so we only need to strip numbers. else if (!isRangedArray && isRoutedToken) { complement = array.filter(function(el) { var type = typeof el; if (type === 'number') { matches[matches.length] = el; return false; } return true; }); } // The final complement is rangedArray with a routedToken, // relative complement is always empty. else { complement = []; matches = array; } return [matches, complement]; }; },{"237":237,"266":266}],266:[function(require,module,exports){ var isArray = Array.isArray; var rangeToArray = require(263); var isNumber = require(293); /** * Takes the first argument, toStrip, and strips it from * the range. The output is an array of ranges that represents * the remaining ranges (relative complement) * * One note. When toStrip is an array, we simply recurse * over each key. Else it requires a lot more logic. * * Since we recurse array keys we are not guaranteed that each strip * item coming in is a string integer. That is why we are doing an isNaN * check. consider: {from: 0, to: 1} and [0, 'one'] intersect at 0, but will * get 'one' fed into stripFromRange. * * @param {Array|String|Number|Object} argToStrip can be a string, number, * or a routed token. Cannot be a range itself. * @param {Range} range * @return {Array.} The relative complement. */ module.exports = function stripFromRange(argToStrip, range) { var ranges = []; var matches = []; var toStrip = argToStrip; // TODO: More than likely a bug around numbers and stripping var toStripIsNumber = isNumber(toStrip); if (toStripIsNumber) { toStrip = +toStrip; } // Strip out NaNs if (!toStripIsNumber && typeof toStrip === 'string') { ranges = [range]; } else if (isArray(toStrip)) { var currenRanges = [range]; toStrip.forEach(function(atom) { var nextRanges = []; currenRanges.forEach(function(currentRename) { var matchAndComplement = stripFromRange(atom, currentRename); if (matchAndComplement[0] !== undefined) { matches = matches.concat(matchAndComplement[0]); } nextRanges = nextRanges.concat(matchAndComplement[1]); }); currenRanges = nextRanges; }); ranges = currenRanges; } // The simple case, its just a number. else if (toStripIsNumber) { if (range.from < toStrip && toStrip < range.to) { ranges[0] = { from: range.from, to: toStrip - 1 }; ranges[1] = { from: toStrip + 1, to: range.to }; matches = [toStrip]; } // In case its a 0 length array. // Even though this assignment is redundant, its point is // to capture the intention. else if (range.from === toStrip && range.to === toStrip) { ranges = []; matches = [toStrip]; } else if (range.from === toStrip) { ranges[0] = { from: toStrip + 1, to: range.to }; matches = [toStrip]; } else if (range.to === toStrip) { ranges[0] = { from: range.from, to: toStrip - 1 }; matches = [toStrip]; } // return the range if no intersection. else { ranges = [range]; } } // Its a routed token. Everything is matched. else { matches = rangeToArray(range); } // If this is a routedToken (Object) then it will match the entire // range since its integers, keys, and ranges. return [matches, ranges]; }; },{"263":263,"293":293}],267:[function(require,module,exports){ var strip = require(264); var catAndSlice = require(288); /** * Takes in the matched path and virtual path and creates the * set of paths that represent the virtualPath being stripped * from the matchedPath. * * @example * Terms: * * Relative Complement: Of sets A and B the relative complement of A in B is * the parts of B that A do not contain. In our example its virtualPath (A) in * matchedPath (B). * * Example: * matchedInput = [[A, D], [B, E], [C, F]] * virtualIntput = [A, Keys, C] * * This will produce 2 arrays from the matched operation. * [ * [D, [B, E], [C, F]], * [A, [B, E], [F]] * ] * * * All the complexity of this function is hidden away in strip and its inner * stripping functions. * @param {PathSet} matchedPath * @param {PathSet} virtualPath */ module.exports = function stripPath(matchedPath, virtualPath) { var relativeComplement = []; var exactMatch = []; var current = []; // Always use virtual path because it can be shorter. for (var i = 0, len = virtualPath.length; i < len; ++i) { var matchedAtom = matchedPath[i]; var virtualAtom = virtualPath[i]; var stripResults = strip(matchedAtom, virtualAtom); var innerMatch = stripResults[0]; var innerComplement = stripResults[1]; var hasComplement = innerComplement.length > 0; // using the algorithm partially described above we need to split and // combine output depending on what comes out of the split function. // 1. If there is no relative complement do no copying / slicing. // 2. If there is both the catAndslice. if (hasComplement) { var flattendIC = innerComplement.length === 1 ? innerComplement[0] : innerComplement; current[i] = flattendIC; relativeComplement[relativeComplement.length] = catAndSlice(current, matchedPath, i + 1); } // The exact match needs to be produced for calling function. exactMatch[i] = innerMatch; current[i] = innerMatch; } return [exactMatch, relativeComplement]; }; },{"264":264,"288":288}],268:[function(require,module,exports){ var convertPathToRoute = require(269); var isPathValue = require(294); var slice = require(299); var isArray = Array.isArray; /** * Creates the named variables and coerces it into its * virtual type. * * @param {Array} route - The route that produced this action wrapper * @private */ function createNamedVariables(route, action) { return function innerCreateNamedVariables(matchedPath) { var convertedArguments; var len = -1; var restOfArgs = slice(arguments, 1); var isJSONObject = !isArray(matchedPath); // A set uses a json object if (isJSONObject) { restOfArgs = []; convertedArguments = matchedPath; } // Could be an array of pathValues for a set operation. else if (isPathValue(matchedPath[0])) { convertedArguments = []; matchedPath.forEach(function(pV) { pV.path = convertPathToRoute(pV.path, route); convertedArguments[++len] = pV; }); } // else just convert and assign else { convertedArguments = convertPathToRoute(matchedPath, route); } return action.apply(this, [convertedArguments].concat(restOfArgs)); }; } module.exports = createNamedVariables; },{"269":269,"294":294,"299":299}],269:[function(require,module,exports){ // Disable eslint for import statements /* eslint-disable max-len */ var Keys = require(237); var convertPathKeyToRange = require(260); var convertPathKeyToIntegers = require(252); var convertPathKeyToKeys = require(253); var isArray = Array.isArray; /* eslint-enable max-len */ /** * takes the path that was matched and converts it to the * virtual path. */ module.exports = function convertPathToRoute(path, route) { var matched = []; // Always use virtual path since path can be longer since // it contains suffixes. for (var i = 0, len = route.length; i < len; ++i) { if (route[i].type) { var virt = route[i]; switch (virt.type) { case Keys.ranges: matched[i] = convertPathKeyToRange(path[i]); break; case Keys.integers: matched[i] = convertPathKeyToIntegers(path[i]); break; case Keys.keys: matched[i] = convertPathKeyToKeys(path[i]); break; default: var err = new Error('Unknown route type.'); err.throwToNext = true; break; } if (virt.named) { matched[virt.name] = matched[matched.length - 1]; } } // Dealing with specific keys or array of specific keys. // If route has an array at this position, arrayify the // path[i] element. else { if (isArray(route[i]) && !isArray(path[i])) { matched[matched.length] = [path[i]]; } else { matched[matched.length] = path[i]; } } } return matched; }; },{"237":237,"252":252,"253":253,"260":260}],270:[function(require,module,exports){ var Keys = require(237); module.exports = function convertTypes(virtualPath) { virtualPath.route = virtualPath.route.map(function(key) { if (typeof key === 'object') { switch (key.type) { case 'keys': key.type = Keys.keys; break; case 'integers': key.type = Keys.integers; break; case 'ranges': key.type = Keys.ranges; break; default: var err = new Error('Unknown route type.'); err.throwToNext = true; break; } } return key; }); }; },{"237":237}],271:[function(require,module,exports){ var Keys = require(237); var actionWrapper = require(268); var pathSyntax = require(125); var convertTypes = require(270); var prettifyRoute = require(298); var errors = require(250); var cloneArray = require(290); var ROUTE_ID = -3; module.exports = function parseTree(routes) { var pTree = {}; var parseMap = {}; routes.forEach(function forEachRoute(route) { // converts the virtual string path to a real path with // extended syntax on. if (typeof route.route === 'string') { route.prettyRoute = route.route; route.route = pathSyntax(route.route, true); convertTypes(route); } if (route.get) { route.getId = ++ROUTE_ID; } if (route.set) { route.setId = ++ROUTE_ID; } if (route.call) { route.callId = ++ROUTE_ID; } setHashOrThrowError(parseMap, route); buildParseTree(pTree, route, 0, []); }); return pTree; }; function buildParseTree(node, routeObject, depth) { var route = routeObject.route; var get = routeObject.get; var set = routeObject.set; var call = routeObject.call; var el = route[depth]; el = !isNaN(+el) && +el || el; var isArray = Array.isArray(el); var i = 0; do { var value = el; var next; if (isArray) { value = value[i]; } // There is a ranged token in this location with / without name. // only happens from parsed path-syntax paths. if (typeof value === 'object') { var routeType = value.type; next = decendTreeByRoutedToken(node, routeType, value); } // This is just a simple key. Could be a ranged key. else { next = decendTreeByRoutedToken(node, value); // we have to create a falcor-router virtual object // so that the rest of the algorithm can match and coerse // when needed. if (next) { route[depth] = {type: value, named: false}; } else { if (!node[value]) { node[value] = {}; } next = node[value]; } } // Continue to recurse or put get/set. if (depth + 1 === route.length) { // Insert match into routeSyntaxTree var matchObject = next[Keys.match] || {}; if (!next[Keys.match]) { next[Keys.match] = matchObject; } matchObject.prettyRoute = routeObject.prettyRoute; if (get) { matchObject.get = actionWrapper(route, get); matchObject.getId = routeObject.getId; } if (set) { matchObject.set = actionWrapper(route, set); matchObject.setId = routeObject.setId; } if (call) { matchObject.call = actionWrapper(route, call); matchObject.callId = routeObject.callId; } } else { buildParseTree(next, routeObject, depth + 1); } } while (isArray && ++i < el.length); } /** * ensure that two routes of the same precedence do not get * set in. */ function setHashOrThrowError(parseMap, routeObject) { var route = routeObject.route; var get = routeObject.get; var set = routeObject.set; var call = routeObject.call; getHashesFromRoute(route). map(function mapHashToString(hash) { return hash.join(','); }). forEach(function forEachRouteHash(hash) { if (get && parseMap[hash + 'get'] || set && parseMap[hash + 'set'] || call && parseMap[hash + 'call']) { throw new Error(errors.routeWithSamePrecedence + ' ' + prettifyRoute(route)); } if (get) { parseMap[hash + 'get'] = true; } if (set) { parseMap[hash + 'set'] = true; } if (call) { parseMap[hash + 'call'] = true; } }); } /** * decends the rst and fills in any naming information at the node. * if what is passed in is not a routed token identifier, then the return * value will be null */ function decendTreeByRoutedToken(node, value, routeToken) { var next = null; switch (value) { case Keys.keys: case Keys.integers: case Keys.ranges: next = node[value]; if (!next) { next = node[value] = {}; } break; default: break; } if (next && routeToken) { // matches the naming information on the node. next[Keys.named] = routeToken.named; next[Keys.name] = routeToken.name; } return next; } /** * creates a hash of the virtual path where integers and ranges * will collide but everything else is unique. */ function getHashesFromRoute(route, depth, hashes, hash) { /*eslint-disable no-func-assign, no-param-reassign*/ depth = depth || 0; hashes = hashes || []; hash = hash || []; /*eslint-enable no-func-assign, no-param-reassign*/ var routeValue = route[depth]; var isArray = Array.isArray(routeValue); var length = isArray && routeValue.length || 0; var idx = 0; var value; if (typeof routeValue === 'object' && !isArray) { value = routeValue.type; } else if (!isArray) { value = routeValue; } do { if (isArray) { value = routeValue[idx]; } if (value === Keys.integers || value === Keys.ranges) { hash[depth] = '__I__'; } else if (value === Keys.keys) { hash[depth] ='__K__'; } else { hash[depth] = value; } // recurse down the routed token if (depth + 1 !== route.length) { getHashesFromRoute(route, depth + 1, hashes, hash); } // Or just add it to hashes else { hashes.push(cloneArray(hash)); } } while (isArray && ++idx < length); return hashes; } },{"125":125,"237":237,"250":250,"268":268,"270":270,"290":290,"298":298}],272:[function(require,module,exports){ var call = 'call'; var runCallAction = require(276); var recurseMatchAndExecute = require(286); var normalizePathSets = require(262); var CallNotFoundError = require(246); var materialize = require(282); var pathUtils = require(155); var collapse = pathUtils.collapse; var Observable = require(240).Observable; var MaxPathsExceededError = require(249); var getPathsCount = require(274); var outputToObservable = require(279); var rxNewToRxNewAndOld = require(280); /** * Performs the call mutation. If a call is unhandled, IE throws error, then * we will chain to the next dataSource in the line. */ module.exports = function routerCall(callPath, args, refPathsArg, thisPathsArg) { var router = this; var source = Observable.defer(function () { var methodSummary; if (router._methodSummaryHook) { methodSummary = { method: 'call', start: router._now(), callPath: callPath, args: args, refPaths: refPathsArg, thisPaths: thisPathsArg, results: [], routes: [] }; } var innerSource = Observable.defer(function() { var refPaths = normalizePathSets(refPathsArg || []); var thisPaths = normalizePathSets(thisPathsArg || []); var jsongCache = {}; var action = runCallAction(router, callPath, args, refPaths, thisPaths, jsongCache, methodSummary); var callPaths = [callPath]; if (getPathsCount(refPaths) + getPathsCount(thisPaths) + getPathsCount(callPaths) > router.maxPaths) { throw new MaxPathsExceededError(); } return recurseMatchAndExecute(router._matcher, action, callPaths, call, router, jsongCache). // Take that map(function(jsongResult) { var reportedPaths = jsongResult.reportedPaths; var jsongEnv = { jsonGraph: jsongResult.jsonGraph }; // Call must report the paths that have been produced. if (reportedPaths.length) { // Collapse the reported paths as they may be inefficient // to send across the wire. jsongEnv.paths = collapse(reportedPaths); } else { jsongEnv.paths = []; jsongEnv.jsonGraph = {}; } // add the invalidated paths to the jsonGraph Envelope var invalidated = jsongResult.invalidated; if (invalidated && invalidated.length) { jsongEnv.invalidated = invalidated; } // Calls are currently materialized. materialize(router, reportedPaths, jsongEnv); return jsongEnv; }). // For us to be able to chain call requests then the error that is // caught has to be a 'function does not exist.' error. From that // we will try the next dataSource in the line. catch(function catchException(e) { if (e instanceof CallNotFoundError && router._unhandled) { return outputToObservable( router._unhandled. call(callPath, args, refPaths, thisPaths)); } throw e; }); }); if (router._methodSummaryHook || router._errorHook) { innerSource = innerSource. do(function (response) { if (router._methodSummaryHook) { methodSummary.results.push({ time: router._now(), value: response }); } }, function (err) { if (router._methodSummaryHook) { methodSummary.error = err; methodSummary.end = router._now(); router._methodSummaryHook(methodSummary); } if (router._errorHook) { router._errorHook(err); } }, function () { if (router._methodSummaryHook) { methodSummary.end = router._now(); router._methodSummaryHook(methodSummary); } }); } return innerSource }); return rxNewToRxNewAndOld(source); }; },{"155":155,"240":240,"246":246,"249":249,"262":262,"274":274,"276":276,"279":279,"280":280,"282":282,"286":286}],273:[function(require,module,exports){ var runGetAction = require(281); var get = 'get'; var recurseMatchAndExecute = require(286); var normalizePathSets = require(262); var materialize = require(282); var Observable = require(240).Observable; var mCGRI = require(283); var MaxPathsExceededError = require(249); var getPathsCount = require(274); var outputToObservable = require(279); var rxNewToRxNewAndOld = require(280); /** * The router get function */ module.exports = function routerGet(paths) { var router = this; return rxNewToRxNewAndOld(Observable.defer(function() { var methodSummary; if (router._methodSummaryHook) { methodSummary = { method: 'get', pathSets: paths, start: router._now(), results: [], routes: [] }; } var result = Observable.defer(function () { var jsongCache = {}; var action = runGetAction(router, jsongCache, methodSummary); var normPS = normalizePathSets(paths); if (getPathsCount(normPS) > router.maxPaths) { throw new MaxPathsExceededError(); } return recurseMatchAndExecute(router._matcher, action, normPS, get, router, jsongCache). // Turn it(jsongGraph, invalidations, missing, etc.) into a // jsonGraph envelope flatMap(function flatMapAfterRouterGet(details) { var out = { jsonGraph: details.jsonGraph }; // If the unhandledPaths are present then we need to // call the backup method for generating materialized. if (details.unhandledPaths.length && router._unhandled) { var unhandledPaths = details.unhandledPaths; // The 3rd argument is the beginning of the actions // arguments, which for get is the same as the // unhandledPaths. return outputToObservable( router._unhandled.get(unhandledPaths)). // Merge the solution back into the overall message. map(function(jsonGraphFragment) { mCGRI(out.jsonGraph, [{ jsonGraph: jsonGraphFragment.jsonGraph, paths: unhandledPaths }], router); return out; }). defaultIfEmpty(out); } return Observable.of(out); }). // We will continue to materialize over the whole jsonGraph message. // This makes sense if you think about pathValues and an API that if // ask for a range of 10 and only 8 were returned, it would not // materialize for you, instead, allow the router to do that. map(function(jsonGraphEnvelope) { return materialize(router, normPS, jsonGraphEnvelope); }); }); if (router._methodSummaryHook || router._errorHook) { result = result. do(function (response) { if (router._methodSummaryHook) { methodSummary.results.push({ time: router._now(), value: response }); } }, function (err) { if (router._methodSummaryHook) { methodSummary.end = router._now(); methodSummary.error = err; router._methodSummaryHook(methodSummary); } if (router._errorHook) { router._errorHook(err) } }, function () { if (router._methodSummaryHook) { methodSummary.end = router._now(); router._methodSummaryHook(methodSummary); } }); } return result; })); }; },{"240":240,"249":249,"262":262,"274":274,"279":279,"280":280,"281":281,"282":282,"283":283,"286":286}],274:[function(require,module,exports){ var falcorPathUtils = require(155); function getPathsCount(pathSets) { return pathSets.reduce(function(numPaths, pathSet) { return numPaths + falcorPathUtils.pathCount(pathSet); }, 0); } module.exports = getPathsCount; },{"155":155}],275:[function(require,module,exports){ var set = 'set'; var recurseMatchAndExecute = require(286); var runSetAction = require(287); var materialize = require(282); var Observable = require(240).Observable; var spreadPaths = require(300); var pathValueMerge = require(245); var optimizePathSets = require(244); var hasIntersectionWithTree = require(257); var getValue = require(242); var normalizePathSets = require(262); var pathUtils = require(155); var collapse = pathUtils.collapse; var mCGRI = require(283); var MaxPathsExceededError = require(249); var getPathsCount = require(274); var outputToObservable = require(279); var rxNewToRxNewAndOld = require(280); /** * @returns {Observable.} * @private */ module.exports = function routerSet(jsonGraph) { var router = this; var source = Observable.defer(function() { var jsongCache = {}; var methodSummary; if (router._methodSummaryHook) { methodSummary = { method: 'set', jsonGraphEnvelope: jsonGraph, start: router._now(), results: [], routes: [] }; } var action = runSetAction(router, jsonGraph, jsongCache, methodSummary); jsonGraph.paths = normalizePathSets(jsonGraph.paths); if (getPathsCount(jsonGraph.paths) > router.maxPaths) { throw new MaxPathsExceededError(); } var innerSource = recurseMatchAndExecute(router._matcher, action, jsonGraph.paths, set, router, jsongCache). // Takes the jsonGraphEnvelope and extra details that comes out // of the recursive matching algorithm and either attempts the // fallback options or returns the built jsonGraph. flatMap(function(details) { var out = { jsonGraph: details.jsonGraph }; // If there is an unhandler then we should call that method and // provide the subset of jsonGraph that represents the missing // routes. if (details.unhandledPaths.length && router._unhandled) { var unhandledPaths = details.unhandledPaths; var jsonGraphFragment = {}; // PERFORMANCE: // We know this is a potential performance downfall // but we want to see if its even a corner case. // Most likely this will not be hit, but if it does // then we can take care of it // Set is interesting. This is what has to happen. // 1. incoming paths are spread so that each one is simple. // 2. incoming path, one at a time, are optimized by the // incoming jsonGraph. // 3. test intersection against incoming optimized path and // unhandledPathSet // 4. If 3 is true, build the jsonGraphFragment by using a // pathValue of optimizedPath and vale from un-optimized // path and original jsonGraphEnvelope. var jsonGraphEnvelope = {jsonGraph: jsonGraphFragment}; var unhandledPathsTree = unhandledPaths. reduce(function(acc, path) { pathValueMerge(acc, {path: path, value: null}); return acc; }, {}); // 1. Spread var pathIntersection = spreadPaths(jsonGraph.paths). // 2.1 Optimize. We know its one at a time therefore we // just pluck [0] out. map(function(path) { return [ // full path path, // optimized path optimizePathSets(details.jsonGraph, [path], router.maxRefFollow)[0]] }). // 2.2 Remove all the optimized paths that were found in // the cache. filter(function(x) { return x[1]; }). // 3.1 test intersection. map(function(pathAndOPath) { var oPath = pathAndOPath[1]; var hasIntersection = hasIntersectionWithTree( oPath, unhandledPathsTree); // Creates the pathValue if there are a path // intersection if (hasIntersection) { var value = getValue(jsonGraph.jsonGraph, pathAndOPath[0]); return { path: oPath, value: value }; } return null; }). // 3.2 strip out nulls (the non-intersection paths). filter(function(x) { return x !== null; }); // 4. build the optimized JSONGraph envelope. pathIntersection. reduce(function(acc, pathValue) { pathValueMerge(acc, pathValue); return acc; }, jsonGraphFragment); jsonGraphEnvelope.paths = collapse( pathIntersection.map(function(pV) { return pV.path; })); return outputToObservable( router._unhandled.set(jsonGraphEnvelope)). // Merge the solution back into the overall message. map(function(unhandledJsonGraphEnv) { mCGRI(out.jsonGraph, [{ jsonGraph: unhandledJsonGraphEnv.jsonGraph, paths: unhandledPaths }], router); return out; }). defaultIfEmpty(out); } return Observable.of(out); }). // We will continue to materialize over the whole jsonGraph message. // This makes sense if you think about pathValues and an API that // if ask for a range of 10 and only 8 were returned, it would not // materialize for you, instead, allow the router to do that. map(function(jsonGraphEnvelope) { return materialize(router, jsonGraph.paths, jsonGraphEnvelope); }); if (router._errorHook || router._methodSummaryHook) { innerSource = innerSource. do( function (response) { if (router._methodSummaryHook) { methodSummary.results.push({ time: router._now(), value: response }); } }, function (err) { if (router._methodSummaryHook) { methodSummary.end = router._now(); methodSummary.error = err; router._methodSummaryHook(methodSummary); } if (router._errorHook) { router._errorHook(err) } }, function () { if (router._methodSummaryHook) { methodSummary.end = router._now(); router._methodSummaryHook(methodSummary); } } ); } return innerSource; }); if (router._errorHook) { source = source. do(null, function summaryHookErrorHandler(err) { router._errorHook(err); }) } return rxNewToRxNewAndOld(source); }; },{"155":155,"240":240,"242":242,"244":244,"245":245,"249":249,"257":257,"262":262,"274":274,"279":279,"280":280,"282":282,"283":283,"286":286,"287":287,"300":300}],276:[function(require,module,exports){ var isJSONG = require(291); var outputToObservable = require(279); var noteToJsongOrPV = require(278); var CallRequiresPathsError = require(247); var mCGRI = require(283); var Observable = require(240).Observable; module.exports = outerRunCallAction; function outerRunCallAction(routerInstance, callPath, args, suffixes, paths, jsongCache, methodSummary) { return function innerRunCallAction(matchAndPath) { return runCallAction(matchAndPath, routerInstance, callPath, args, suffixes, paths, jsongCache, methodSummary); }; } function runCallAction(matchAndPath, routerInstance, callPath, args, suffixes, paths, jsongCache, methodSummary) { var match = matchAndPath.match; var matchedPath = matchAndPath.path; var out; // We are at out destination. Its time to get out // the pathValues from the if (match.isCall) { // This is where things get interesting out = Observable. defer(function() { var next; try { next = match. action.call( routerInstance, matchedPath, args, suffixes, paths); } catch (e) { e.throwToNext = true; throw e; } var output = outputToObservable(next); if (methodSummary) { var route = { start: routerInstance._now(), route: matchAndPath.match.prettyRoute, pathSet: matchAndPath.path, results: [] }; methodSummary.routes.push(route); output = output.do( function (response) { route.results.push({ time: routerInstance._now(), value: response }); }, function (err) { route.error = err; route.end = routerInstance._now(); }, function () { route.end = routerInstance._now(); } ) } return output.toArray(); }). // Required to get the references from the outputting jsong // and pathValues. map(function(res) { // checks call for isJSONG and if there is jsong without paths // throw errors. var refs = []; var values = []; // Will flatten any arrays of jsong/pathValues. var callOutput = res. // Filters out any falsy values filter(function(x) { return x; }). reduce(function(flattenedRes, next) { return flattenedRes.concat(next); }, []); // An empty output from call if (callOutput.length === 0) { return []; } var refLen = -1; callOutput.forEach(function(r) { // its json graph. if (isJSONG(r)) { // This is a hard error and must fully stop the server if (!r.paths) { var err = new CallRequiresPathsError(); err.throwToNext = true; throw err; } } }); var invsRefsAndValues = mCGRI(jsongCache, callOutput, routerInstance); invsRefsAndValues.references.forEach(function(ref) { refs[++refLen] = ref; }); values = invsRefsAndValues.values.map(function(pv) { return pv.path; }); var callLength = callOutput.length; var callPathSave1 = callPath.slice(0, callPath.length - 1); var hasSuffixes = suffixes && suffixes.length; var hasPaths = paths && paths.length; // We are going to use recurseMatchAndExecute to run // the paths and suffixes for call. For that to happen // we must send a message to the outside to switch from // call to get. callOutput[++callLength] = {isMessage: true, method: 'get'}; // If there are paths to add then push them into the next // paths through 'additionalPaths' message. if (hasPaths && (callLength + 1)) { paths.forEach(function(path) { callOutput[++callLength] = { isMessage: true, additionalPath: callPathSave1.concat(path) }; }); } // Suffix is the same as paths except for how to calculate // a path per reference found from the callPath. if (hasSuffixes) { // matchedPath is the optimized path to call. // e.g: // callPath: [genreLists, 0, add] -> // matchedPath: [lists, 'abc', add] var optimizedPathLength = matchedPath.length - 1; // For every reference build the complete path // from the callPath - 1 and concat remaining // path from the PathReference (path is where the // reference was found, not the value of the reference). // e.g: from the above example the output is: // output = {path: [lists, abc, 0], value: [titles, 123]} // // This means the refs object = [output]; // callPathSave1: [genreLists, 0], // optimizedPathLength: 3 - 1 = 2 // ref.path.slice(2): [lists, abc, 0].slice(2) = [0] // deoptimizedPath: [genreLists, 0, 0] // // Add the deoptimizedPath to the callOutput messages. // This will make the outer expand run those as a 'get' refs.forEach(function(ref) { var deoptimizedPath = callPathSave1.concat( ref.path.slice(optimizedPathLength)); suffixes.forEach(function(suffix) { var additionalPath = deoptimizedPath.concat(suffix); callOutput[++callLength] = { isMessage: true, additionalPath: additionalPath }; }); }); } // If there are no suffixes but there are references, report // the paths to the references. There may be values as well, // add those to the output. if (refs.length && !hasSuffixes || values.length) { var additionalPaths = []; if (refs.length && !hasSuffixes) { additionalPaths = refs. map(function(x) { return x.path; }); } additionalPaths. concat(values). forEach(function(path) { callOutput[++callLength] = { isMessage: true, additionalPath: path }; }); } return callOutput; }). // When call has an error it needs to be propagated to the next // level instead of onCompleted'ing do(null, function(e) { e.throwToNext = true; throw e; }); } else { out = Observable.defer(function () { return outputToObservable( match.action.call(routerInstance, matchAndPath.path) ); }); if (methodSummary) { var route = { start: routerInstance._now(), route: matchAndPath.match.prettyRoute, pathSet: matchAndPath.path, results: [] }; methodSummary.routes.push(route); out = out.do( function (response) { route.results.push({ time: routerInstance._now(), value: response }); }, function (err) { route.error = err; route.end = routerInstance._now(); }, function () { route.end = routerInstance._now(); } ) } } return out. materialize(). filter(function(note) { return note.kind !== 'C'; }). map(noteToJsongOrPV(matchAndPath.path, false, routerInstance)). map(function(jsonGraphOrPV) { return [matchAndPath.match, jsonGraphOrPV]; }); } },{"240":240,"247":247,"278":278,"279":279,"283":283,"291":291}],277:[function(require,module,exports){ var JSONGraphError = require(248); module.exports = function errorToPathValue(error, path) { var typeValue = { $type: 'error', value: {} }; if (error.throwToNext) { throw error; } // If it is a special JSONGraph error then pull all the data if (error instanceof JSONGraphError) { typeValue = error.typeValue; } else if (error instanceof Error) { typeValue.value.message = error.message; } return { path: path, value: typeValue }; }; },{"248":248}],278:[function(require,module,exports){ var isJSONG = require(291); var onNext = 'N'; var errorToPathValue = require(277); /** * Takes a path and for every onNext / onError it will attempt * to pluck the value or error from the note and process it * with the path object passed in. * @param {PathSet|PathSet[]} pathOrPathSet - * @param {Boolean} isPathSet - */ module.exports = function noteToJsongOrPV(pathOrPathSet, isPathSet, routerInstance) { return function(note) { return convertNoteToJsongOrPV( pathOrPathSet, note, isPathSet, routerInstance ); }; }; function convertNoteToJsongOrPV(pathOrPathSet, note, isPathSet, routerInstance) { var incomingJSONGOrPathValues; var kind = note.kind; // Take what comes out of the function and assume its either a pathValue or // jsonGraph. if (kind === onNext) { incomingJSONGOrPathValues = note.value; } // Convert the error to a pathValue. else { incomingJSONGOrPathValues = errorToPathValue(note.error, pathOrPathSet); if (routerInstance._errorHook) { routerInstance._errorHook(note.error); } } // If its jsong we may need to optionally attach the // paths if the paths do not exist if (isJSONG(incomingJSONGOrPathValues) && !incomingJSONGOrPathValues.paths) { incomingJSONGOrPathValues = { jsonGraph: incomingJSONGOrPathValues.jsonGraph, paths: isPathSet && pathOrPathSet || [pathOrPathSet] }; } return incomingJSONGOrPathValues; } },{"277":277,"291":291}],279:[function(require,module,exports){ var Observable = require(240).Observable; var isArray = Array.isArray; var $$observable = require(310).default; /** * For the router there are several return types from user * functions. The standard set are: synchronous type (boolean or * json graph) or an async type (observable or a thenable). */ module.exports = function outputToObservable(valueOrObservable) { var value = valueOrObservable; // if it's one of OUR observables, great. if (value instanceof Observable) { return value; } // falsy value if (!value) { return Observable.of(value); } // lowercase-o observables, 3rd party observables if (value[$$observable]) { return Observable.from(value); } // Rx4 and lower observables if (value.subscribe) { var oldObservable = value; return Observable.create(function(observer) { var oldObserver = { onNext: function (v) { this.observer.next(v); }, onError: function (err) { this.observer.error(err); }, onCompleted: function () { this.observer.complete(); }, observer: observer }; var oldSubscription = oldObservable.subscribe(oldObserver); return function () { oldSubscription.dispose(); }; }); } // promises if (value.then) { return Observable.from(value); } // from array of pathValues. if (isArray(value)) { return Observable.of(value); } // this will be jsong or pathValue at this point. // NOTE: For the case of authorize this will be a boolean return Observable.of(value); }; },{"240":240,"310":310}],280:[function(require,module,exports){ "use strict"; function noop() {} function toRxNewObserver(observer) { var onNext = observer.onNext; var onError = observer.onError; var onCompleted = observer.onCompleted; if ( typeof onNext !== "function" && typeof onError !== "function" && typeof onCompleted !== "function" ) { return observer; } // old observer! return { next: typeof onNext === "function" ? function(x) { this.destination.onNext(x); } : noop, error: typeof onError === "function" ? function(err) { this.destination.onError(err); } : noop, complete: typeof onCompleted === "function" ? function() { this.destination.onCompleted(); } : noop, destination: observer }; } // WHY NOT BOTH? module.exports = function rxNewToRxNewAndOld(rxNewObservable) { var _subscribe = rxNewObservable.subscribe; rxNewObservable.subscribe = function(observerOrNextFn, errFn, compFn) { var subscription; if (typeof observerOrNextFn !== "object" || observerOrNextFn === null) { subscription = _subscribe.call( this, observerOrNextFn, errFn, compFn ); } else { var observer = toRxNewObserver(observerOrNextFn); subscription = _subscribe.call(this, observer); } var _unsubscribe = subscription.unsubscribe; subscription.unsubscribe = subscription.dispose = function() { this.isDisposed = true; _unsubscribe.call(this); }; return subscription; }; return rxNewObservable; }; },{}],281:[function(require,module,exports){ var outputToObservable = require(279); var noteToJsongOrPV = require(278); var Observable = require(240).Observable; module.exports = function runGetAction(routerInstance, jsongCache, methodSummary) { return function innerGetAction(matchAndPath) { return getAction(routerInstance, matchAndPath, jsongCache, methodSummary); }; }; function getAction(routerInstance, matchAndPath, jsongCache, methodSummary) { var match = matchAndPath.match; var out; try { out = match.action.call(routerInstance, matchAndPath.path); out = outputToObservable(out); if (methodSummary) { var _out = out; out = Observable.defer(function () { var route = { start: routerInstance._now(), route: matchAndPath.match.prettyRoute, pathSet: matchAndPath.path, results: [] }; methodSummary.routes.push(route); return _out.do(function (response) { route.results.push({ time: routerInstance._now(), value: response }); }, function (err) { route.error = err; route.end = routerInstance._now(); }, function () { route.end = routerInstance._now(); }); }) } } catch (e) { out = Observable.throw(e); } return out. materialize(). filter(function(note) { return note.kind !== 'C'; }). map(noteToJsongOrPV(matchAndPath.path, false, routerInstance)). map(function(jsonGraphOrPV) { return [matchAndPath.match, jsonGraphOrPV]; }); } },{"240":240,"278":278,"279":279}],282:[function(require,module,exports){ var pathValueMerge = require(245); var optimizePathSets = require(244); var $atom = require(301).$atom; /** * given a set of paths and a jsonGraph envelope, materialize missing will * crawl all the paths to ensure that they have been fully filled in. The * paths that are missing will be filled with materialized atoms. */ module.exports = function materializeMissing(router, paths, jsongEnv) { var jsonGraph = jsongEnv.jsonGraph; var materializedAtom = {$type: $atom}; // Optimizes the pathSets from the jsong then // inserts atoms of undefined. optimizePathSets(jsonGraph, paths, router.maxRefFollow). forEach(function(optMissingPath) { pathValueMerge(jsonGraph, { path: optMissingPath, value: materializedAtom }); }); return {jsonGraph: jsonGraph}; } },{"244":244,"245":245,"301":301}],283:[function(require,module,exports){ var jsongMerge = require(243); var pathValueMerge = require(245); var isJSONG = require(291); var isMessage = require(292); module.exports = mergeCacheAndGatherRefsAndInvalidations; /** * takes the response from an action and merges it into the * cache. Anything that is an invalidation will be added to * the first index of the return value, and the inserted refs * are the second index of the return value. The third index * of the return value is messages from the action handlers * * @param {Object} cache * @param {Array} jsongOrPVs */ function mergeCacheAndGatherRefsAndInvalidations( cache, jsongOrPVs, routerInstance ) { var references = []; var len = -1; var invalidations = []; var unhandledPaths = []; var messages = []; var values = []; // Go through each of the outputs from the route end point and separate out // each type of potential output. // // * There are values that need to be merged into the JSONGraphCache // * There are references that need to be merged and potentially followed // * There are messages that can alter the behavior of the // recurseMatchAndExecute cycle. // * unhandledPaths happens when a path matches a route but the route does // not match the entire path, therefore there is unmatched paths. jsongOrPVs.forEach(function(jsongOrPV) { var refsAndValues = []; if (isMessage(jsongOrPV)) { messages[messages.length] = jsongOrPV; } else if (isJSONG(jsongOrPV)) { refsAndValues = jsongMerge(cache, jsongOrPV, routerInstance); } // Last option are path values. else { refsAndValues = pathValueMerge(cache, jsongOrPV); } var refs = refsAndValues.references; var vals = refsAndValues.values; var invs = refsAndValues.invalidations; var unhandled = refsAndValues.unhandledPaths; if (vals && vals.length) { values = values.concat(vals); } if (invs && invs.length) { invalidations = invalidations.concat(invs); } if (unhandled && unhandled.length) { unhandledPaths = unhandledPaths.concat(unhandled); } if (refs && refs.length) { refs.forEach(function(ref) { references[++len] = ref; }); } }); return { invalidations: invalidations, references: references, messages: messages, values: values, unhandledPaths: unhandledPaths }; } },{"243":243,"245":245,"291":291,"292":292}],284:[function(require,module,exports){ /* eslint-disable max-len */ var pathUtils = require(155); var collapse = pathUtils.collapse; var stripPath = require(267); var hasIntersection = require(256); /* eslint-enable max-len */ /** * takes in the set of ordered matches and pathSet that got those matches. * From there it will give back a list of matches to execute. */ module.exports = function getExecutableMatches(matches, pathSet) { var remainingPaths = pathSet; var matchAndPaths = []; var out = { matchAndPaths: matchAndPaths, unhandledPaths: false }; for (var i = 0; i < matches.length && remainingPaths.length > 0; ++i) { var availablePaths = remainingPaths; var match = matches[i]; remainingPaths = []; if (i > 0) { availablePaths = collapse(availablePaths); } // For every available path attempt to intersect. If there // is an intersection then strip and replace. // any relative complements, add to remainingPaths for (var j = 0; j < availablePaths.length; ++j) { var path = availablePaths[j]; if (hasIntersection(path, match.virtual)) { var stripResults = stripPath(path, match.virtual); matchAndPaths[matchAndPaths.length] = { path: stripResults[0], match: match }; remainingPaths = remainingPaths.concat(stripResults[1]); } else if (i < matches.length - 1) { remainingPaths[remainingPaths.length] = path; } } } // Adds the remaining paths to the unhandled paths section. if (remainingPaths && remainingPaths.length) { out.unhandledPaths = remainingPaths; } return out; }; },{"155":155,"256":256,"267":267}],285:[function(require,module,exports){ var Observable = require(240).Observable; var getExecutableMatches = require(284); /** * Sorts and strips the set of available matches given the pathSet. */ module.exports = function runByPrecedence(pathSet, matches, actionRunner) { // Precendence matching var sortedMatches = matches. sort(function(a, b) { if (a.precedence < b.precedence) { return 1; } else if (a.precedence > b.precedence) { return -1; } return 0; }); var execs = getExecutableMatches(sortedMatches, [pathSet]); var setOfMatchedPaths = Observable. from(execs.matchAndPaths). flatMap(actionRunner). // Note: We do not wait for each observable to finish, // but repeat the cycle per onNext. map(function(actionTuple) { return { match: actionTuple[0], value: actionTuple[1] }; }); if (execs.unhandledPaths) { setOfMatchedPaths = setOfMatchedPaths. concat(Observable.of({ match: {suffix: []}, value: { isMessage: true, unhandledPaths: execs.unhandledPaths } })); } return setOfMatchedPaths; }; },{"240":240,"284":284}],286:[function(require,module,exports){ var Rx = require(240); var Observable = Rx.Observable; var runByPrecedence = require(285); var pathUtils = require(155); var collapse = pathUtils.collapse; var optimizePathSets = require(244); var mCGRI = require(283); var isArray = Array.isArray; /** * The recurse and match function will async recurse as long as * there are still more paths to be executed. The match function * will return a set of objects that have how much of the path that * is matched. If there still is more, denoted by suffixes, * paths to be matched then the recurser will keep running. */ module.exports = function recurseMatchAndExecute( match, actionRunner, paths, method, routerInstance, jsongCache) { return _recurseMatchAndExecute( match, actionRunner, paths, method, routerInstance, jsongCache); }; /** * performs the actual recursing */ function _recurseMatchAndExecute( match, actionRunner, paths, method, routerInstance, jsongCache) { var unhandledPaths = []; var invalidated = []; var reportedPaths = []; var currentMethod = method; return Observable. // Each pathSet (some form of collapsed path) need to be sent // independently. for each collapsed pathSet will, if producing // refs, be the highest likelihood of collapsibility. from(paths). expand(function(nextPaths) { if (!nextPaths.length) { return Observable.empty(); } // We have to return an Observable of error instead of just // throwing. var matchedResults; try { matchedResults = match(currentMethod, nextPaths); } catch (e) { return Observable.throw(e); } // When there is explicitly not a match then we need to handle // the unhandled paths. if (!matchedResults.length) { unhandledPaths.push(nextPaths); return Observable.empty(); } return runByPrecedence(nextPaths, matchedResults, actionRunner). // Generate from the combined results the next requestable paths // and insert errors / values into the cache. flatMap(function(results) { var value = results.value; var suffix = results.match.suffix; // TODO: MaterializedPaths, use result.path to build up a // "foundPaths" array. This could be used to materialize // if that is the case. I don't think this is a // requirement, but it could be. if (!isArray(value)) { value = [value]; } var invsRefsAndValues = mCGRI(jsongCache, value, routerInstance); var invalidations = invsRefsAndValues.invalidations; var unhandled = invsRefsAndValues.unhandledPaths; var messages = invsRefsAndValues.messages; var pathsToExpand = []; if (suffix.length > 0) { pathsToExpand = invsRefsAndValues.references; } // Merge the invalidations and unhandledPaths. invalidations.forEach(function(invalidation) { invalidated[invalidated.length] = invalidation.path; }); unhandled.forEach(function(unhandledPath) { unhandledPaths[unhandledPaths.length] = unhandledPath; }); // Merges the remaining suffix with remaining nextPaths pathsToExpand = pathsToExpand.map(function(next) { return next.value.concat(suffix); }); // Alters the behavior of the expand messages.forEach(function(message) { // mutates the method type for the matcher if (message.method) { currentMethod = message.method; } // Mutates the nextPaths and adds any additionalPaths else if (message.additionalPath) { var path = message.additionalPath; pathsToExpand[pathsToExpand.length] = path; reportedPaths[reportedPaths.length] = path; } // Any invalidations that come down from a call else if (message.invalidations) { message. invalidations. forEach(function(invalidation) { invalidated.push(invalidation); }); } // We need to add the unhandledPaths to the jsonGraph // response. else if (message.unhandledPaths) { unhandledPaths = unhandledPaths. concat(message.unhandledPaths); } }); // Explodes and collapse the tree to remove // redundants and get optimized next set of // paths to evaluate. pathsToExpand = optimizePathSets( jsongCache, pathsToExpand, routerInstance.maxRefFollow); if (pathsToExpand.length) { pathsToExpand = collapse(pathsToExpand); } return Observable. from(pathsToExpand); }). defaultIfEmpty([]); }, Number.POSITIVE_INFINITY, Rx.Scheduler.queue). reduce(function(acc, x) { return acc; }, null). map(function() { return { unhandledPaths: unhandledPaths, invalidated: invalidated, jsonGraph: jsongCache, reportedPaths: reportedPaths }; }); } },{"155":155,"240":240,"244":244,"283":283,"285":285}],287:[function(require,module,exports){ /* eslint-disable max-len */ var outputToObservable = require(279); var noteToJsongOrPV = require(278); var spreadPaths = require(300); var getValue = require(242); var jsongMerge = require(243); var optimizePathSets = require(244); var hasIntersection = require(256); var pathValueMerge = require(245); var Observable = require(240).Observable; /* eslint-enable max-len */ module.exports = function outerRunSetAction(routerInstance, modelContext, jsongCache, methodSummary) { return function innerRunSetAction(matchAndPath) { return runSetAction(routerInstance, modelContext, matchAndPath, jsongCache, methodSummary); }; }; function runSetAction(routerInstance, jsongMessage, matchAndPath, jsongCache, methodSummary) { var match = matchAndPath.match; var out; var arg = matchAndPath.path; // We are at out destination. Its time to get out // the pathValues from the if (match.isSet) { var paths = spreadPaths(jsongMessage.paths); // We have to ensure that the paths maps in order // to the optimized paths array. var optimizedPathsAndPaths = paths. // Optimizes each path. map(function(path) { return [optimizePathSets( jsongCache, [path], routerInstance.maxRefFollow)[0], path]; }). // only includes the paths from the set that intersect // the virtual path filter(function(optimizedAndPath) { return optimizedAndPath[0] && hasIntersection(optimizedAndPath[0], match.virtual); }); var optimizedPaths = optimizedPathsAndPaths.map(function(opp) { return opp[0]; }); var subSetPaths = optimizedPathsAndPaths.map(function(opp) { return opp[1]; }); var tmpJsonGraph = subSetPaths. reduce(function(json, path, i) { pathValueMerge(json, { path: optimizedPaths[i], value: getValue(jsongMessage.jsonGraph, path) }); return json; }, {}); // Takes the temporary JSONGraph, attaches only the matched paths // then creates the subset json and assigns it to the argument to // the set function. var subJsonGraphEnv = { jsonGraph: tmpJsonGraph, paths: [match.requested] }; arg = {}; jsongMerge(arg, subJsonGraphEnv, routerInstance); } try { out = match.action.call(routerInstance, arg); out = outputToObservable(out); if (methodSummary) { var _out = out; out = Observable.defer(function () { var route = { route: matchAndPath.match.prettyRoute, pathSet: matchAndPath.path, start: routerInstance._now() }; methodSummary.routes.push(route); return _out.do( function (result) { route.results = route.results || []; route.results.push({ time: routerInstance._now(), value: result }); }, function (err) { route.error = err; route.end = routerInstance._now(); }, function () { route.end = routerInstance._now(); } ) }); } } catch (e) { out = Observable.throw(e); } return out. materialize(). filter(function(note) { return note.kind !== 'C'; }). map(noteToJsongOrPV(matchAndPath.path, false, routerInstance)). map(function(jsonGraphOrPV) { return [matchAndPath.match, jsonGraphOrPV]; }); } },{"240":240,"242":242,"243":243,"244":244,"245":245,"256":256,"278":278,"279":279,"300":300}],288:[function(require,module,exports){ module.exports = function catAndSlice(a, b, slice) { var next = [], i, j, len; for (i = 0, len = a.length; i < len; ++i) { next[i] = a[i]; } for (j = slice || 0, len = b.length; j < len; ++j, ++i) { next[i] = b[j]; } return next; }; },{}],289:[function(require,module,exports){ module.exports = function copy(valueType) { if ((typeof valueType !== 'object') || (valueType === null)) { return valueType; } return Object. keys(valueType). reduce(function(acc, k) { acc[k] = valueType[k]; return acc; }, {}); }; },{}],290:[function(require,module,exports){ function cloneArray(arr, index) { var a = []; var len = arr.length; for (var i = index || 0; i < len; i++) { a[i] = arr[i]; } return a; } module.exports = cloneArray; },{}],291:[function(require,module,exports){ module.exports = function isJSONG(x) { return x.jsonGraph; }; },{}],292:[function(require,module,exports){ module.exports = function isMessage(output) { return output.hasOwnProperty('isMessage'); }; },{}],293:[function(require,module,exports){ /** * Will determine of the argument is a number. * * '1' returns true * 1 returns true * [1] returns false * null returns false * @param {*} x */ module.exports = function(x) { return String(Number(x)) === String(x) && typeof x !== 'object'; }; },{}],294:[function(require,module,exports){ module.exports = function(x) { return x.hasOwnProperty('path') && x.hasOwnProperty('value'); }; },{}],295:[function(require,module,exports){ module.exports = function isRange(range) { return range.hasOwnProperty('to') && range.hasOwnProperty('from'); }; },{}],296:[function(require,module,exports){ /** * Determines if the object is a routed token by hasOwnProperty * of type and named */ module.exports = function isRoutedToken(obj) { return obj.hasOwnProperty('type') && obj.hasOwnProperty('named'); }; },{}],297:[function(require,module,exports){ module.exports = String.fromCharCode(30); },{}],298:[function(require,module,exports){ var Keys = require(237); /** * beautify the virtual path, meaning paths with virtual keys will * not be displayed as a stringified object but instead as a string. * * @param {Array} route - */ module.exports = function prettifyRoute(route) { var length = 0; var str = []; for (var i = 0, len = route.length; i < len; ++i, ++length) { var value = route[i]; if (typeof value === 'object') { value = value.type; } if (value === Keys.integers) { str[length] = 'integers'; } else if (value === Keys.ranges) { str[length] = 'ranges'; } else if (value === Keys.keys) { str[length] = 'keys'; } else { if (Array.isArray(value)) { str[length] = JSON.stringify(value); } else { str[length] = value; } } } return str; } },{"237":237}],299:[function(require,module,exports){ module.exports = function slice(args, index) { var len = args.length; var out = []; var j = 0; var i = index; while (i < len) { out[j] = args[i]; ++i; ++j; } return out; }; },{}],300:[function(require,module,exports){ var iterateKeySet = require(155).iterateKeySet; var cloneArray = require(290); /** * Takes in a ptahSet and will create a set of simple paths. * @param {Array} paths */ module.exports = function spreadPaths(paths) { var allPaths = []; paths.forEach(function(x) { _spread(x, 0, allPaths); }); return allPaths; }; function _spread(pathSet, depth, out, currentPath) { /* eslint-disable no-param-reassign */ currentPath = currentPath || []; /* eslint-enable no-param-reassign */ if (depth === pathSet.length) { out[out.length] = cloneArray(currentPath); return; } // Simple case var key = pathSet[depth]; if (typeof key !== 'object') { currentPath[depth] = key; _spread(pathSet, depth + 1, out, currentPath); return; } // complex key. var iteratorNote = {}; var innerKey = iterateKeySet(key, iteratorNote); do { // spreads the paths currentPath[depth] = innerKey; _spread(pathSet, depth + 1, out, currentPath); currentPath.length = depth; innerKey = iterateKeySet(key, iteratorNote); } while (!iteratorNote.done); } },{"155":155,"290":290}],301:[function(require,module,exports){ module.exports = { $ref: 'ref', $atom: 'atom', $error: 'error' }; },{}],302:[function(require,module,exports){ 'use strict'; module.exports = require(307) },{"307":307}],303:[function(require,module,exports){ (function (Promise){(function (){ 'use strict'; var asap = require(115); function noop() {} // States: // // 0 - pending // 1 - fulfilled with _value // 2 - rejected with _value // 3 - adopted the state of another promise, _value // // once the state is no longer pending (0) it is immutable // All `_` prefixed properties will be reduced to `_{random number}` // at build time to obfuscate them and discourage their use. // We don't use symbols or Object.defineProperty to fully hide them // because the performance isn't good enough. // to avoid using try/catch inside critical functions, we // extract them to here. var LAST_ERROR = null; var IS_ERROR = {}; function getThen(obj) { try { return obj.then; } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } function tryCallOne(fn, a) { try { return fn(a); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } function tryCallTwo(fn, a, b) { try { fn(a, b); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } module.exports = Promise; function Promise(fn) { if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } if (typeof fn !== 'function') { throw new TypeError('Promise constructor\'s argument is not a function'); } this._U = 0; this._V = 0; this._W = null; this._X = null; if (fn === noop) return; doResolve(fn, this); } Promise._Y = null; Promise._Z = null; Promise._0 = noop; Promise.prototype.then = function(onFulfilled, onRejected) { if (this.constructor !== Promise) { return safeThen(this, onFulfilled, onRejected); } var res = new Promise(noop); handle(this, new Handler(onFulfilled, onRejected, res)); return res; }; function safeThen(self, onFulfilled, onRejected) { return new self.constructor(function (resolve, reject) { var res = new Promise(noop); res.then(resolve, reject); handle(self, new Handler(onFulfilled, onRejected, res)); }); } function handle(self, deferred) { while (self._V === 3) { self = self._W; } if (Promise._Y) { Promise._Y(self); } if (self._V === 0) { if (self._U === 0) { self._U = 1; self._X = deferred; return; } if (self._U === 1) { self._U = 2; self._X = [self._X, deferred]; return; } self._X.push(deferred); return; } handleResolved(self, deferred); } function handleResolved(self, deferred) { asap(function() { var cb = self._V === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { if (self._V === 1) { resolve(deferred.promise, self._W); } else { reject(deferred.promise, self._W); } return; } var ret = tryCallOne(cb, self._W); if (ret === IS_ERROR) { reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); } function resolve(self, newValue) { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) { return reject( self, new TypeError('A promise cannot be resolved with itself.') ); } if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { var then = getThen(newValue); if (then === IS_ERROR) { return reject(self, LAST_ERROR); } if ( then === self.then && newValue instanceof Promise ) { self._V = 3; self._W = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(then.bind(newValue), self); return; } } self._V = 1; self._W = newValue; finale(self); } function reject(self, newValue) { self._V = 2; self._W = newValue; if (Promise._Z) { Promise._Z(self, newValue); } finale(self); } function finale(self) { if (self._U === 1) { handle(self, self._X); self._X = null; } if (self._U === 2) { for (var i = 0; i < self._X.length; i++) { handle(self, self._X[i]); } self._X = null; } } function Handler(onFulfilled, onRejected, promise){ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, promise) { var done = false; var res = tryCallTwo(fn, function (value) { if (done) return; done = true; resolve(promise, value); }, function (reason) { if (done) return; done = true; reject(promise, reason); }); if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } } }).call(this)}).call(this,typeof Promise === "function" ? Promise : require(302)) },{"115":115,"302":302}],304:[function(require,module,exports){ 'use strict'; var Promise = require(303); module.exports = Promise; Promise.prototype.done = function (onFulfilled, onRejected) { var self = arguments.length ? this.then.apply(this, arguments) : this; self.then(null, function (err) { setTimeout(function () { throw err; }, 0); }); }; },{"303":303}],305:[function(require,module,exports){ 'use strict'; //This file contains the ES6 extensions to the core Promises/A+ API var Promise = require(303); module.exports = Promise; /* Static Functions */ var TRUE = valuePromise(true); var FALSE = valuePromise(false); var NULL = valuePromise(null); var UNDEFINED = valuePromise(undefined); var ZERO = valuePromise(0); var EMPTYSTRING = valuePromise(''); function valuePromise(value) { var p = new Promise(Promise._0); p._V = 1; p._W = value; return p; } Promise.resolve = function (value) { if (value instanceof Promise) return value; if (value === null) return NULL; if (value === undefined) return UNDEFINED; if (value === true) return TRUE; if (value === false) return FALSE; if (value === 0) return ZERO; if (value === '') return EMPTYSTRING; if (typeof value === 'object' || typeof value === 'function') { try { var then = value.then; if (typeof then === 'function') { return new Promise(then.bind(value)); } } catch (ex) { return new Promise(function (resolve, reject) { reject(ex); }); } } return valuePromise(value); }; var iterableToArray = function (iterable) { if (typeof Array.from === 'function') { // ES2015+, iterables exist iterableToArray = Array.from; return Array.from(iterable); } // ES5, only arrays and array-likes exist iterableToArray = function (x) { return Array.prototype.slice.call(x); }; return Array.prototype.slice.call(iterable); } Promise.all = function (arr) { var args = iterableToArray(arr); return new Promise(function (resolve, reject) { if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { if (val instanceof Promise && val.then === Promise.prototype.then) { while (val._V === 3) { val = val._W; } if (val._V === 1) return res(i, val._W); if (val._V === 2) reject(val._W); val.then(function (val) { res(i, val); }, reject); return; } else { var then = val.then; if (typeof then === 'function') { var p = new Promise(then.bind(val)); p.then(function (val) { res(i, val); }, reject); return; } } } args[i] = val; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; Promise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); }; Promise.race = function (values) { return new Promise(function (resolve, reject) { iterableToArray(values).forEach(function(value){ Promise.resolve(value).then(resolve, reject); }); }); }; /* Prototype Methods */ Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); }; },{"303":303}],306:[function(require,module,exports){ 'use strict'; var Promise = require(303); module.exports = Promise; Promise.prototype.finally = function (f) { return this.then(function (value) { return Promise.resolve(f()).then(function () { return value; }); }, function (err) { return Promise.resolve(f()).then(function () { throw err; }); }); }; },{"303":303}],307:[function(require,module,exports){ 'use strict'; module.exports = require(303); require(304); require(306); require(305); require(308); require(309); },{"303":303,"304":304,"305":305,"306":306,"308":308,"309":309}],308:[function(require,module,exports){ 'use strict'; // This file contains then/promise specific extensions that are only useful // for node.js interop var Promise = require(303); var asap = require(114); module.exports = Promise; /* Static Functions */ Promise.denodeify = function (fn, argumentCount) { if ( typeof argumentCount === 'number' && argumentCount !== Infinity ) { return denodeifyWithCount(fn, argumentCount); } else { return denodeifyWithoutCount(fn); } }; var callbackFn = ( 'function (err, res) {' + 'if (err) { rj(err); } else { rs(res); }' + '}' ); function denodeifyWithCount(fn, argumentCount) { var args = []; for (var i = 0; i < argumentCount; i++) { args.push('a' + i); } var body = [ 'return function (' + args.join(',') + ') {', 'var self = this;', 'return new Promise(function (rs, rj) {', 'var res = fn.call(', ['self'].concat(args).concat([callbackFn]).join(','), ');', 'if (res &&', '(typeof res === "object" || typeof res === "function") &&', 'typeof res.then === "function"', ') {rs(res);}', '});', '};' ].join(''); return Function(['Promise', 'fn'], body)(Promise, fn); } function denodeifyWithoutCount(fn) { var fnLength = Math.max(fn.length - 1, 3); var args = []; for (var i = 0; i < fnLength; i++) { args.push('a' + i); } var body = [ 'return function (' + args.join(',') + ') {', 'var self = this;', 'var args;', 'var argLength = arguments.length;', 'if (arguments.length > ' + fnLength + ') {', 'args = new Array(arguments.length + 1);', 'for (var i = 0; i < arguments.length; i++) {', 'args[i] = arguments[i];', '}', '}', 'return new Promise(function (rs, rj) {', 'var cb = ' + callbackFn + ';', 'var res;', 'switch (argLength) {', args.concat(['extra']).map(function (_, index) { return ( 'case ' + (index) + ':' + 'res = fn.call(' + ['self'].concat(args.slice(0, index)).concat('cb').join(',') + ');' + 'break;' ); }).join(''), 'default:', 'args[argLength] = cb;', 'res = fn.apply(self, args);', '}', 'if (res &&', '(typeof res === "object" || typeof res === "function") &&', 'typeof res.then === "function"', ') {rs(res);}', '});', '};' ].join(''); return Function( ['Promise', 'fn'], body )(Promise, fn); } Promise.nodeify = function (fn) { return function () { var args = Array.prototype.slice.call(arguments); var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null; var ctx = this; try { return fn.apply(this, arguments).nodeify(callback, ctx); } catch (ex) { if (callback === null || typeof callback == 'undefined') { return new Promise(function (resolve, reject) { reject(ex); }); } else { asap(function () { callback.call(ctx, ex); }) } } } }; Promise.prototype.nodeify = function (callback, ctx) { if (typeof callback != 'function') return this; this.then(function (value) { asap(function () { callback.call(ctx, null, value); }); }, function (err) { asap(function () { callback.call(ctx, err); }); }); }; },{"114":114,"303":303}],309:[function(require,module,exports){ 'use strict'; var Promise = require(303); module.exports = Promise; Promise.enableSynchronous = function () { Promise.prototype.isPending = function() { return this.getState() == 0; }; Promise.prototype.isFulfilled = function() { return this.getState() == 1; }; Promise.prototype.isRejected = function() { return this.getState() == 2; }; Promise.prototype.getValue = function () { if (this._V === 3) { return this._W.getValue(); } if (!this.isFulfilled()) { throw new Error('Cannot get a value of an unfulfilled promise.'); } return this._W; }; Promise.prototype.getReason = function () { if (this._V === 3) { return this._W.getReason(); } if (!this.isRejected()) { throw new Error('Cannot get a rejection reason of a non-rejected promise.'); } return this._W; }; Promise.prototype.getState = function () { if (this._V === 3) { return this._W.getState(); } if (this._V === -1 || this._V === -2) { return 0; } return this._V; }; }; Promise.disableSynchronous = function() { Promise.prototype.isPending = undefined; Promise.prototype.isFulfilled = undefined; Promise.prototype.isRejected = undefined; Promise.prototype.getValue = undefined; Promise.prototype.getReason = undefined; Promise.prototype.getState = undefined; }; },{"303":303}],310:[function(require,module,exports){ (function (global){(function (){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _ponyfill = require(311); var _ponyfill2 = _interopRequireDefault(_ponyfill); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var root; /* global window */ if (typeof self !== 'undefined') { root = self; } else if (typeof window !== 'undefined') { root = window; } else if (typeof global !== 'undefined') { root = global; } else if (typeof module !== 'undefined') { root = module; } else { root = Function('return this')(); } var result = (0, _ponyfill2['default'])(root); exports['default'] = result; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"311":311}],311:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports['default'] = symbolObservablePonyfill; function symbolObservablePonyfill(root) { var result; var _Symbol = root.Symbol; if (typeof _Symbol === 'function') { if (_Symbol.observable) { result = _Symbol.observable; } else { result = _Symbol('observable'); _Symbol.observable = result; } } else { result = '@@observable'; } return result; }; },{}]},{},[1])(1) }); ================================================ FILE: dist/falcor.browser.js ================================================ /*! * Copyright 2020 Netflix, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.falcor = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i} - the requested data as JSON */ Model.prototype.get = require(57); /** * _getOptimizedBoundPath is an extension point for internal users to polyfill * legacy soft-bind behavior, as opposed to deref (hardBind). Current falcor * only supports deref, and assumes _path to be a fully optimized path. * @function * @private * @return {Path} - fully optimized bound path for the model */ Model.prototype._getOptimizedBoundPath = function _getOptimizedBoundPath() { return this._path ? this._path.slice() : this._path; }; /** * The get method retrieves several {@link Path}s or {@link PathSet}s from a {@link Model}. The get method loads each value into a JSON object and returns in a ModelResponse. * @function * @private * @param {Array.} paths - the path(s) to retrieve * @return {ModelResponse.} - the requested data as JSON */ Model.prototype._getWithPaths = require(56); /** * Sets the value at one or more places in the JSONGraph model. The set method accepts one or more {@link PathValue}s, each of which is a combination of a location in the document and the value to place there. In addition to accepting {@link PathValue}s, the set method also returns the values after the set operation is complete. * @function * @return {ModelResponse.} - an {@link Observable} stream containing the values in the JSONGraph model after the set was attempted */ Model.prototype.set = require(60); /** * The preload method retrieves several {@link Path}s or {@link PathSet}s from a {@link Model} and loads them into the Model cache. * @function * @param {...PathSet} path - the path(s) to retrieve * @return {ModelResponse.} - a ModelResponse that completes when the data has been loaded into the cache. */ Model.prototype.preload = function preload() { var out = validateInput(arguments, GET_VALID_INPUT, "preload"); if (out !== true) { return new ModelResponse(function(o) { o.onError(out); }); } var args = Array.prototype.slice.call(arguments); var self = this; return new ModelResponse(function(obs) { return self.get.apply(self, args).subscribe( function() {}, function(err) { obs.onError(err); }, function() { obs.onCompleted(); } ); }); }; /** * Invokes a function in the JSON Graph. * @function * @param {Path} functionPath - the path to the function to invoke * @param {Array.} args - the arguments to pass to the function * @param {Array.} refPaths - the paths to retrieve from the JSON Graph References in the message returned from the function * @param {Array.} extraPaths - additional paths to retrieve after successful function execution * @return {ModelResponse. - a JSONEnvelope contains the values returned from the function */ Model.prototype.call = function call() { var args; var argsIdx = -1; var argsLen = arguments.length; args = new Array(argsLen); while (++argsIdx < argsLen) { var arg = arguments[argsIdx]; args[argsIdx] = arg; var argType = typeof arg; if ( (argsIdx > 1 && !Array.isArray(arg)) || (argsIdx === 0 && !Array.isArray(arg) && argType !== "string") || (argsIdx === 1 && !Array.isArray(arg) && !isPrimitive(arg)) ) { /* eslint-disable no-loop-func */ return new ModelResponse(function(o) { o.onError(new Error("Invalid argument")); }); /* eslint-enable no-loop-func */ } } return new CallResponse(this, args[0], args[1], args[2], args[3]); }; /** * The invalidate method synchronously removes several {@link Path}s or {@link PathSet}s from a {@link Model} cache. * @function * @param {...PathSet} path - the paths to remove from the {@link Model}'s cache. */ Model.prototype.invalidate = function invalidate() { var args; var argsIdx = -1; var argsLen = arguments.length; args = []; while (++argsIdx < argsLen) { args[argsIdx] = pathSyntax.fromPath(arguments[argsIdx]); if (!Array.isArray(args[argsIdx]) || !args[argsIdx].length) { throw new Error("Invalid argument"); } } // creates the obs, subscribes and will throw the errors if encountered. new InvalidateResponse(this, args).subscribe(noOp, function(e) { throw e; }); }; /** * Returns a new {@link Model} bound to a location within the {@link * JSONGraph}. The bound location is never a {@link Reference}: any {@link * Reference}s encountered while resolving the bound {@link Path} are always * replaced with the {@link Reference}s target value. For subsequent operations * on the {@link Model}, all paths will be evaluated relative to the bound * path. Deref allows you to: * - Expose only a fragment of the {@link JSONGraph} to components, rather than * the entire graph * - Hide the location of a {@link JSONGraph} fragment from components * - Optimize for executing multiple operations and path looksup at/below the * same location in the {@link JSONGraph} * @method * @param {Object} responseObject - an object previously retrieved from the * Model * @return {Model} - the dereferenced {@link Model} * @example var Model = falcor.Model; var model = new Model({ cache: { users: [ Model.ref(["usersById", 32]) ], usersById: { 32: { name: "Steve", surname: "McGuire" } } } }); model. get(['users', 0, 'name']). subscribe(function(jsonEnv) { var userModel = model.deref(jsonEnv.json.users[0]); console.log(model.getPath()); console.log(userModel.getPath()); }); }); // prints the following: // [] // ["usersById", 32] - because userModel refers to target of reference at ["users", 0] */ Model.prototype.deref = require(6); /** * A dereferenced model can become invalid when the reference from which it was * built has been removed/collected/expired/etc etc. To fix the issue, a from * the parent request should be made (no parent, then from the root) for a valid * path and re-dereference performed to update what the model is bound too. * * @method * @private * @return {Boolean} - If the currently deref'd model is still considered a * valid deref. */ Model.prototype._hasValidParentReference = require(5); /** * Get data for a single {@link Path}. * @param {Path} path - the path to retrieve * @return {Observable.<*>} - the value for the path * @example var model = new falcor.Model({source: new HttpDataSource("/model.json") }); model. getValue('user.name'). subscribe(function(name) { console.log(name); }); // The code above prints "Jim" to the console. */ Model.prototype.getValue = require(20); /** * Set value for a single {@link Path}. * @param {Path} path - the path to set * @param {Object} value - the value to set * @return {Observable.<*>} - the value for the path * @example var model = new falcor.Model({source: new HttpDataSource("/model.json") }); model. setValue('user.name', 'Jim'). subscribe(function(name) { console.log(name); }); // The code above prints "Jim" to the console. */ Model.prototype.setValue = require(69); // TODO: Does not throw if given a PathSet rather than a Path, not sure if it should or not. // TODO: Doc not accurate? I was able to invoke directly against the Model, perhaps because I don't have a data source? // TODO: Not clear on what it means to "retrieve objects in addition to JSONGraph values" /** * Synchronously retrieves a single path from the local {@link Model} only and will not retrieve missing paths from the {@link DataSource}. This method can only be invoked when the {@link Model} does not have a {@link DataSource} or from within a selector function. See {@link Model.prototype.get}. The getValueSync method differs from the asynchronous get methods (ex. get, getValues) in that it can be used to retrieve objects in addition to JSONGraph values. * @method * @private * @arg {Path} path - the path to retrieve * @return {*} - the value for the specified path */ Model.prototype._getValueSync = require(28); /** * @private */ Model.prototype._setValueSync = require(70); /** * @private */ Model.prototype._derefSync = require(7); /** * Set the local cache to a {@link JSONGraph} fragment. This method can be a useful way of mocking a remote document, or restoring the local cache from a previously stored state. * @param {JSONGraph} jsonGraph - the {@link JSONGraph} fragment to use as the local cache */ Model.prototype.setCache = function modelSetCache(cacheOrJSONGraphEnvelope) { var cache = this._root.cache; if (cacheOrJSONGraphEnvelope !== cache) { var modelRoot = this._root; var boundPath = this._path; this._path = []; this._root.cache = {}; if (typeof cache !== "undefined") { collectLru(modelRoot, modelRoot.expired, getSize(cache), 0); } var out; if (isJSONGraphEnvelope(cacheOrJSONGraphEnvelope)) { out = setJSONGraphs(this, [cacheOrJSONGraphEnvelope])[0]; } else if (isJSONEnvelope(cacheOrJSONGraphEnvelope)) { out = setCache(this, [cacheOrJSONGraphEnvelope])[0]; } else if (isObject(cacheOrJSONGraphEnvelope)) { out = setCache(this, [{ json: cacheOrJSONGraphEnvelope }])[0]; } // performs promotion without producing output. if (out) { get.getWithPathsAsPathMap(this, out, []); } this._path = boundPath; } else if (typeof cache === "undefined") { this._root.cache = {}; } return this; }; /** * Get the local {@link JSONGraph} cache. This method can be a useful to store the state of the cache. * @param {...Array.} [pathSets] - The path(s) to retrieve. If no paths are specified, the entire {@link JSONGraph} is returned. * @return {JSONGraph} all of the {@link JSONGraph} data in the {@link Model} cache. * @example // Storing the boxshot of the first 10 titles in the first 10 genreLists to local storage. localStorage.setItem('cache', JSON.stringify(model.getCache("genreLists[0...10][0...10].boxshot"))); */ Model.prototype.getCache = function _getCache() { var paths = Array.prototype.slice.call(arguments); if (paths.length === 0) { return getCache(this._root.cache); } var result = [{}]; var path = this._path; get.getWithPathsAsJSONGraph(this, paths, result); this._path = path; return result[0].jsonGraph; }; /** * Reset cache maxSize. When the new maxSize is smaller than the old force a collect. * @param {Number} maxSize - the new maximum cache size */ Model.prototype._setMaxSize = function setMaxSize(maxSize) { var oldMaxSize = this._maxSize; this._maxSize = maxSize; if (maxSize < oldMaxSize) { var modelRoot = this._root; var modelCache = modelRoot.cache; // eslint-disable-next-line no-cond-assign var currentVersion = modelCache.$_version; collectLru( modelRoot, modelRoot.expired, getSize(modelCache), this._maxSize, this._collectRatio, currentVersion ); } }; /** * Retrieves a number which is incremented every single time a value is changed underneath the Model or the object at an optionally-provided Path beneath the Model. * @param {Path?} path - a path at which to retrieve the version number * @return {Number} a version number which changes whenever a value is changed underneath the Model or provided Path */ Model.prototype.getVersion = function getVersion(pathArg) { var path = (pathArg && pathSyntax.fromPath(pathArg)) || []; if (Array.isArray(path) === false) { throw new Error("Model#getVersion must be called with an Array path."); } if (this._path.length) { path = this._path.concat(path); } return this._getVersion(this, path); }; Model.prototype._syncCheck = function syncCheck(name) { if (Boolean(this._source) && this._root.syncRefCount <= 0 && this._root.unsafeMode === false) { throw new Error("Model#" + name + " may only be called within the context of a request selector."); } return true; }; /* eslint-disable guard-for-in */ Model.prototype._clone = function cloneModel(opts) { var clone = new this.constructor(this); for (var key in opts) { var value = opts[key]; if (value === "delete") { delete clone[key]; } else { clone[key] = value; } } clone.setCache = void 0; return clone; }; /* eslint-enable */ /** * Returns a clone of the {@link Model} that enables batching. Within the configured time period, * paths for get operations are collected and sent to the {@link DataSource} in a batch. Batching * can be more efficient if the {@link DataSource} access the network, potentially reducing the * number of HTTP requests to the server. * * @param {?Scheduler|number} schedulerOrDelay - Either a {@link Scheduler} that determines when to * send a batch to the {@link DataSource}, or the number in milliseconds to collect a batch before * sending to the {@link DataSource}. If this parameter is omitted, then batch collection ends at * the end of the next tick. * @return {Model} a Model which schedules a batch of get requests to the DataSource. */ Model.prototype.batch = function batch(schedulerOrDelay) { var scheduler; if (typeof schedulerOrDelay === "number") { scheduler = new TimeoutScheduler(Math.round(Math.abs(schedulerOrDelay))); } else if (!schedulerOrDelay || !schedulerOrDelay.schedule) { scheduler = new TimeoutScheduler(1); } else { scheduler = schedulerOrDelay; } var clone = this._clone(); clone._request = new RequestQueue(clone, scheduler); return clone; }; /** * Returns a clone of the {@link Model} that disables batching. This is the default mode. Each get operation will be executed on the {@link DataSource} separately. * @name unbatch * @memberof Model.prototype * @function * @return {Model} a {@link Model} that batches requests of the same type and sends them to the data source together */ Model.prototype.unbatch = function unbatch() { var clone = this._clone(); clone._request = new RequestQueue(clone, new ImmediateScheduler()); return clone; }; /** * Returns a clone of the {@link Model} that treats errors as values. Errors will be reported in the same callback used to report data. Errors will appear as objects in responses, rather than being sent to the {@link Observable~onErrorCallback} callback of the {@link ModelResponse}. * @return {Model} */ Model.prototype.treatErrorsAsValues = function treatErrorsAsValues() { return this._clone({ _treatErrorsAsValues: true }); }; /** * Adapts a Model to the {@link DataSource} interface. * @return {DataSource} * @example var model = new falcor.Model({ cache: { user: { name: "Steve", surname: "McGuire" } } }), proxyModel = new falcor.Model({ source: model.asDataSource() }); // Prints "Steve" proxyModel.getValue("user.name"). then(function(name) { console.log(name); }); */ Model.prototype.asDataSource = function asDataSource() { return new ModelDataSourceAdapter(this); }; Model.prototype._materialize = function materialize() { return this._clone({ _materialized: true }); }; Model.prototype._dematerialize = function dematerialize() { return this._clone({ _materialized: "delete" }); }; /** * Returns a clone of the {@link Model} that boxes values returning the wrapper ({@link Atom}, {@link Reference}, or {@link Error}), rather than the value inside it. This allows any metadata attached to the wrapper to be inspected. * @return {Model} */ Model.prototype.boxValues = function boxValues() { return this._clone({ _boxed: true }); }; /** * Returns a clone of the {@link Model} that unboxes values, returning the value inside of the wrapper ({@link Atom}, {@link Reference}, or {@link Error}), rather than the wrapper itself. This is the default mode. * @return {Model} */ Model.prototype.unboxValues = function unboxValues() { return this._clone({ _boxed: "delete" }); }; /** * Returns a clone of the {@link Model} that only uses the local {@link JSONGraph} and never uses a {@link DataSource} to retrieve missing paths. * @return {Model} */ Model.prototype.withoutDataSource = function withoutDataSource() { return this._clone({ _source: "delete" }); }; Model.prototype.toJSON = function toJSON() { return { $type: "ref", value: this._path }; }; /** * Returns the {@link Path} to the object within the JSON Graph that this Model references. * @return {Path} * @example var Model = falcor.Model; var model = new Model({ cache: { users: [ Model.ref(["usersById", 32]) ], usersById: { 32: { name: "Steve", surname: "McGuire" } } } }); model. get(['users', 0, 'name']). subscribe(function(jsonEnv) { var userModel = model.deref(jsonEnv.json.users[0]); console.log(model.getPath()); console.log(userModel.getPath()); }); }); // prints the following: // [] // ["usersById", 32] - because userModel refers to target of reference at ["users", 0] */ Model.prototype.getPath = function getPath() { return this._path ? this._path.slice() : this._path; }; /** * This one is actually private. I would not use this without talking to * jhusain, sdesai, or michaelbpaulson (github). * @private */ Model.prototype._fromWhenceYouCame = function fromWhenceYouCame(allow) { return this._clone({ _allowFromWhenceYouCame: allow === undefined ? true : allow }); }; Model.prototype._getBoundValue = require(17); Model.prototype._getVersion = require(22); Model.prototype._getPathValuesAsPathMap = get.getWithPathsAsPathMap; Model.prototype._getPathValuesAsJSONG = get.getWithPathsAsJSONGraph; Model.prototype._setPathValues = require(68); Model.prototype._setPathMaps = require(67); Model.prototype._setJSONGs = require(66); Model.prototype._setCache = require(67); Model.prototype._invalidatePathValues = require(38); Model.prototype._invalidatePathMaps = require(37); },{"105":105,"120":120,"124":124,"17":17,"18":18,"20":20,"22":22,"23":23,"28":28,"3":3,"37":37,"38":38,"39":39,"4":4,"43":43,"49":49,"5":5,"50":50,"51":51,"56":56,"57":57,"58":58,"6":6,"60":60,"64":64,"65":65,"66":66,"67":67,"68":68,"69":69,"7":7,"70":70,"77":77,"87":87,"88":88,"89":89,"91":91}],3:[function(require,module,exports){ function ModelDataSourceAdapter(model) { this._model = model._materialize().treatErrorsAsValues(); } ModelDataSourceAdapter.prototype.get = function get(pathSets) { return this._model.get.apply(this._model, pathSets)._toJSONG(); }; ModelDataSourceAdapter.prototype.set = function set(jsongResponse) { return this._model.set(jsongResponse)._toJSONG(); }; ModelDataSourceAdapter.prototype.call = function call(path, args, suffixes, paths) { var params = [path, args, suffixes]; Array.prototype.push.apply(params, paths); return this._model.call.apply(this._model, params)._toJSONG(); }; module.exports = ModelDataSourceAdapter; },{}],4:[function(require,module,exports){ var isFunction = require(85); var hasOwn = require(80); function ModelRoot(o) { var options = o || {}; this.syncRefCount = 0; this.expired = options.expired || []; this.unsafeMode = options.unsafeMode || false; this.cache = {}; if (isFunction(options.comparator)) { this.comparator = options.comparator; } if (isFunction(options.errorSelector)) { this.errorSelector = options.errorSelector; } if (isFunction(options.onChange)) { this.onChange = options.onChange; } } ModelRoot.prototype.errorSelector = function errorSelector(x, y) { return y; }; ModelRoot.prototype.comparator = function comparator(cacheNode, messageNode) { if (hasOwn(cacheNode, "value") && hasOwn(messageNode, "value")) { // They are the same only if the following fields are the same. return cacheNode.value === messageNode.value && cacheNode.$type === messageNode.$type && cacheNode.$expires === messageNode.$expires; } return cacheNode === messageNode; }; module.exports = ModelRoot; },{"80":80,"85":85}],5:[function(require,module,exports){ module.exports = function fromWhenceYeCame() { var reference = this._referenceContainer; // Always true when this mode is false. if (!this._allowFromWhenceYouCame) { return true; } // If fromWhenceYouCame is true and the first set of keys did not have // a reference, this case can happen. They are always valid. if (reference === true) { return true; } // was invalid before even derefing. if (reference === false) { return false; } // Its been disconnected (set over or collected) from the graph. // eslint-disable-next-line camelcase if (reference && reference.$_parent === undefined) { return false; } // The reference has expired but has not been collected from the graph. // eslint-disable-next-line camelcase if (reference && reference.$_invalidated) { return false; } return true; }; },{}],6:[function(require,module,exports){ var InvalidDerefInputError = require(9); var getCachePosition = require(19); var CONTAINER_DOES_NOT_EXIST = "e"; var $ref = require(110); module.exports = function deref(boundJSONArg) { var absolutePath = boundJSONArg && boundJSONArg.$__path; var refPath = boundJSONArg && boundJSONArg.$__refPath; var toReference = boundJSONArg && boundJSONArg.$__toReference; var referenceContainer; // We deref and then ensure that the reference container is attached to // the model. if (absolutePath) { var validContainer = CONTAINER_DOES_NOT_EXIST; if (toReference) { validContainer = false; referenceContainer = getCachePosition(this, toReference); // If the reference container is still a sentinel value then compare // the reference value with refPath. If they are the same, then the // model is still valid. if (refPath && referenceContainer && referenceContainer.$type === $ref) { var containerPath = referenceContainer.value; var i = 0; var len = refPath.length; validContainer = true; for (; validContainer && i < len; ++i) { if (containerPath[i] !== refPath[i]) { validContainer = false; } } } } // Signal to the deref'd model that it has been disconnected from the // graph or there is no _fromWhenceYouCame if (!validContainer) { referenceContainer = false; } // The container did not exist, therefore there is no reference // container and fromWhenceYouCame should always return true. else if (validContainer === CONTAINER_DOES_NOT_EXIST) { referenceContainer = true; } return this._clone({ _path: absolutePath, _referenceContainer: referenceContainer }); } throw new InvalidDerefInputError(); }; },{"110":110,"19":19,"9":9}],7:[function(require,module,exports){ var pathSyntax = require(124); var getBoundValue = require(17); var InvalidModelError = require(10); module.exports = function derefSync(boundPathArg) { var boundPath = pathSyntax.fromPath(boundPathArg); if (!Array.isArray(boundPath)) { throw new Error("Model#derefSync must be called with an Array path."); } var boundValue = getBoundValue(this, this._path.concat(boundPath), false); var path = boundValue.path; var node = boundValue.value; var found = boundValue.found; // If the node is not found or the node is found but undefined is returned, // this happens when a reference is expired. if (!found || node === undefined) { return undefined; } if (node.$type) { throw new InvalidModelError(path, path); } return this._clone({ _path: path }); }; },{"10":10,"124":124,"17":17}],8:[function(require,module,exports){ var applyErrorPrototype = require(14); /** * When a bound model attempts to retrieve JSONGraph it should throw an * error. * * @private */ function BoundJSONGraphModelError() { var instance = new Error("It is not legal to use the JSON Graph " + "format from a bound Model. JSON Graph format" + " can only be used from a root model."); instance.name = "BoundJSONGraphModelError"; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, BoundJSONGraphModelError); } return instance; } applyErrorPrototype(BoundJSONGraphModelError); module.exports = BoundJSONGraphModelError; },{"14":14}],9:[function(require,module,exports){ var applyErrorPrototype = require(14); /** * An invalid deref input is when deref is used with input that is not generated * from a get, set, or a call. * * @private */ function InvalidDerefInputError() { var instance = new Error("Deref can only be used with a non-primitive object from get, set, or call."); instance.name = "InvalidDerefInputError"; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, InvalidDerefInputError); } return instance; } applyErrorPrototype(InvalidDerefInputError); module.exports = InvalidDerefInputError; },{"14":14}],10:[function(require,module,exports){ var applyErrorPrototype = require(14); /** * An InvalidModelError can only happen when a user binds, whether sync * or async to shorted value. See the unit tests for examples. * * @param {*} boundPath * @param {*} shortedPath * * @private */ function InvalidModelError(boundPath, shortedPath) { var instance = new Error("The boundPath of the model is not valid since a value or error was found before the path end."); instance.name = "InvalidModelError"; instance.boundPath = boundPath; instance.shortedPath = shortedPath; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, InvalidModelError); } return instance; } applyErrorPrototype(InvalidModelError); module.exports = InvalidModelError; },{"14":14}],11:[function(require,module,exports){ var applyErrorPrototype = require(14); /** * InvalidSourceError happens when a dataSource syncronously throws * an exception during a get/set/call operation. * * @param {Error} error - The error that was thrown. * * @private */ function InvalidSourceError(error) { var instance = new Error("An exception was thrown when making a request."); instance.name = "InvalidSourceError"; instance.innerError = error; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, InvalidSourceError); } return instance; } applyErrorPrototype(InvalidSourceError); module.exports = InvalidSourceError; },{"14":14}],12:[function(require,module,exports){ var applyErrorPrototype = require(14); /** * A request can only be retried up to a specified limit. Once that * limit is exceeded, then an error will be thrown. * * @param {*} missingOptimizedPaths * * @private */ function MaxRetryExceededError(missingOptimizedPaths) { var instance = new Error("The allowed number of retries have been exceeded."); instance.name = "MaxRetryExceededError"; instance.missingOptimizedPaths = missingOptimizedPaths || []; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, MaxRetryExceededError); } return instance; } applyErrorPrototype(MaxRetryExceededError); MaxRetryExceededError.is = function(e) { return e && e.name === "MaxRetryExceededError"; }; module.exports = MaxRetryExceededError; },{"14":14}],13:[function(require,module,exports){ var applyErrorPrototype = require(14); /** * Does not allow null in path * * @private * @param {Object} [options] - Optional object containing additional error information * @param {Array} [options.requestedPath] - The path that was being processed when the error occurred */ function NullInPathError(options) { var requestedPathString = options && options.requestedPath && options.requestedPath.join ? options.requestedPath.join(", ") : ""; var instance = new Error("`null` and `undefined` are not allowed in branch key positions for requested path: " + requestedPathString); instance.name = "NullInPathError"; if (Object.setPrototypeOf) { Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); } if (Error.captureStackTrace) { Error.captureStackTrace(instance, NullInPathError); } return instance; } applyErrorPrototype(NullInPathError); module.exports = NullInPathError; },{"14":14}],14:[function(require,module,exports){ function applyErrorPrototype(errorType) { errorType.prototype = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf) { Object.setPrototypeOf(errorType, Error); } else { // eslint-disable-next-line errorType.__proto__ = Error; } } module.exports = applyErrorPrototype; },{}],15:[function(require,module,exports){ var createHardlink = require(73); var onValue = require(26); var isExpired = require(30); var $ref = require(110); var promote = require(40); /* eslint-disable no-constant-condition */ function followReference(model, root, nodeArg, referenceContainerArg, referenceArg, seed, isJSONG) { var node = nodeArg; var reference = referenceArg; var referenceContainer = referenceContainerArg; var depth = 0; var k, next; while (true) { if (depth === 0 && referenceContainer.$_context) { depth = reference.length; next = referenceContainer.$_context; } else { k = reference[depth++]; next = node[k]; } if (next) { var type = next.$type; var value = type && next.value || next; if (depth < reference.length) { if (type) { node = next; break; } node = next; continue; } // We need to report a value or follow another reference. else { node = next; if (type && isExpired(next)) { break; } if (!referenceContainer.$_context) { createHardlink(referenceContainer, next); } // Restart the reference follower. if (type === $ref) { // Nulls out the depth, outerResults, if (isJSONG) { onValue(model, next, seed, null, null, null, null, reference, reference.length, isJSONG); } else { promote(model._root, next); } depth = 0; reference = value; referenceContainer = next; node = root; continue; } break; } } else { node = void 0; } break; } if (depth < reference.length && node !== void 0) { var ref = []; for (var i = 0; i < depth; i++) { ref[i] = reference[i]; } reference = ref; } return [node, reference, referenceContainer]; } /* eslint-enable */ module.exports = followReference; },{"110":110,"26":26,"30":30,"40":40,"73":73}],16:[function(require,module,exports){ var getCachePosition = require(19); var InvalidModelError = require(10); var BoundJSONGraphModelError = require(8); function mergeInto(target, obj) { /* eslint guard-for-in: 0 */ if (target === obj) { return; } if (target === null || typeof target !== "object" || target.$type) { return; } if (obj === null || typeof obj !== "object" || obj.$type) { return; } for (var key in obj) { // When merging over a temporary branch structure (for example, as produced by an error selector) // with references, we don't want to mutate the path, particularly because it's also $_absolutePath // on cache nodes if (key === "$__path") { continue; } var targetValue = target[key]; if (targetValue === undefined) { target[key] = obj[key]; } else { mergeInto(targetValue, obj[key]); } } } function defaultEnvelope(isJSONG) { return isJSONG ? {jsonGraph: {}, paths: []} : {json: {}}; } module.exports = function get(walk, isJSONG) { return function innerGet(model, paths, seed) { // Result valueNode not immutable for isJSONG. var nextSeed = isJSONG ? seed : [{}]; var valueNode = nextSeed[0]; var results = { values: nextSeed, optimizedPaths: [] }; var cache = model._root.cache; var boundPath = model._path; var currentCachePosition = cache; var optimizedPath, optimizedLength; var i, len; var requestedPath = []; var derefInfo = []; var referenceContainer; // If the model is bound, then get that cache position. if (boundPath.length) { // JSONGraph output cannot ever be bound or else it will // throw an error. if (isJSONG) { return { criticalError: new BoundJSONGraphModelError() }; } // using _getOptimizedPath because that's a point of extension // for polyfilling legacy falcor optimizedPath = model._getOptimizedBoundPath(); optimizedLength = optimizedPath.length; // We need to get the new cache position path. currentCachePosition = getCachePosition(model, optimizedPath); // If there was a short, then we 'throw an error' to the outside // calling function which will onError the observer. if (currentCachePosition && currentCachePosition.$type) { return { criticalError: new InvalidModelError(boundPath, optimizedPath) }; } referenceContainer = model._referenceContainer; } // Update the optimized path if we else { optimizedPath = []; optimizedLength = 0; } for (i = 0, len = paths.length; i < len; i++) { walk(model, cache, currentCachePosition, paths[i], 0, valueNode, results, derefInfo, requestedPath, optimizedPath, optimizedLength, isJSONG, false, referenceContainer); } // Merge in existing results. // Default to empty envelope if no results were emitted mergeInto(valueNode, paths.length ? seed[0] : defaultEnvelope(isJSONG)); return results; }; }; },{"10":10,"19":19,"8":8}],17:[function(require,module,exports){ var getValueSync = require(21); var InvalidModelError = require(10); module.exports = function getBoundValue(model, pathArg, materialized) { var path = pathArg; var boundPath = pathArg; var boxed, treatErrorsAsValues, value, shorted, found; boxed = model._boxed; materialized = model._materialized; treatErrorsAsValues = model._treatErrorsAsValues; model._boxed = true; model._materialized = materialized === undefined || materialized; model._treatErrorsAsValues = true; value = getValueSync(model, path.concat(null), true); model._boxed = boxed; model._materialized = materialized; model._treatErrorsAsValues = treatErrorsAsValues; path = value.optimizedPath; shorted = value.shorted; found = value.found; value = value.value; while (path.length && path[path.length - 1] === null) { path.pop(); } if (found && shorted) { throw new InvalidModelError(boundPath, path); } return { path: path, value: value, shorted: shorted, found: found }; }; },{"10":10,"21":21}],18:[function(require,module,exports){ var isInternalKey = require(86); /** * decends and copies the cache. */ module.exports = function getCache(cache) { var out = {}; _copyCache(cache, out); return out; }; function cloneBoxedValue(boxedValue) { var clonedValue = {}; var keys = Object.keys(boxedValue); var key; var i; var l; for (i = 0, l = keys.length; i < l; i++) { key = keys[i]; if (!isInternalKey(key)) { clonedValue[key] = boxedValue[key]; } } return clonedValue; } function _copyCache(node, out, fromKey) { // copy and return Object. keys(node). filter(function(k) { // Its not an internal key and the node has a value. In the cache // there are 3 possibilities for values. // 1: A branch node. // 2: A $type-value node. // 3: undefined // We will strip out 3 return !isInternalKey(k) && node[k] !== undefined; }). forEach(function(key) { var cacheNext = node[key]; var outNext = out[key]; if (!outNext) { outNext = out[key] = {}; } // Paste the node into the out cache. if (cacheNext.$type) { var isObject = cacheNext.value && typeof cacheNext.value === "object"; var isUserCreatedcacheNext = !cacheNext.$_modelCreated; var value; if (isObject || isUserCreatedcacheNext) { value = cloneBoxedValue(cacheNext); } else { value = cacheNext.value; } out[key] = value; return; } _copyCache(cacheNext, outNext, key); }); } },{"86":86}],19:[function(require,module,exports){ /** * getCachePosition makes a fast walk to the bound value since all bound * paths are the most possible optimized path. * * @param {Model} model - * @param {Array} path - * @returns {Mixed} - undefined if there is nothing in this position. * @private */ module.exports = function getCachePosition(model, path) { var currentCachePosition = model._root.cache; var depth = -1; var maxDepth = path.length; // The loop is simple now, we follow the current cache position until // while (++depth < maxDepth && currentCachePosition && !currentCachePosition.$type) { currentCachePosition = currentCachePosition[path[depth]]; } return currentCachePosition; }; },{}],20:[function(require,module,exports){ var ModelResponse = require(51); var pathSyntax = require(124); module.exports = function getValue(path) { var parsedPath = pathSyntax.fromPath(path); var pathIdx = 0; var pathLen = parsedPath.length; while (++pathIdx < pathLen) { if (typeof parsedPath[pathIdx] === "object") { /* eslint-disable no-loop-func */ return new ModelResponse(function(o) { o.onError(new Error("Paths must be simple paths")); }); /* eslint-enable no-loop-func */ } } var self = this; return new ModelResponse(function(obs) { return self.get(parsedPath).subscribe(function(data) { var curr = data.json; var depth = -1; var length = parsedPath.length; while (curr && ++depth < length) { curr = curr[parsedPath[depth]]; } obs.onNext(curr); }, function(err) { obs.onError(err); }, function() { obs.onCompleted(); }); }); }; },{"124":124,"51":51}],21:[function(require,module,exports){ var followReference = require(15); var clone = require(29); var isExpired = require(30); var promote = require(40); var $ref = require(110); var $atom = require(108); var $error = require(109); module.exports = function getValueSync(model, simplePath, noClone) { var root = model._root.cache; var len = simplePath.length; var optimizedPath = []; var shorted = false, shouldShort = false; var depth = 0; var key, i, next = root, curr = root, out = root, type, ref, refNode; var found = true; var expired = false; while (next && depth < len) { key = simplePath[depth++]; if (key !== null) { next = curr[key]; optimizedPath[optimizedPath.length] = key; } if (!next) { out = undefined; shorted = true; found = false; break; } type = next.$type; // A materialized item. There is nothing to deref to. if (type === $atom && next.value === undefined) { out = undefined; found = false; shorted = depth < len; break; } // Up to the last key we follow references, ensure that they are not // expired either. if (depth < len) { if (type === $ref) { // If the reference is expired then we need to set expired to // true. if (isExpired(next)) { expired = true; out = undefined; break; } ref = followReference(model, root, root, next, next.value); refNode = ref[0]; // The next node is also set to undefined because nothing // could be found, this reference points to nothing, so // nothing must be returned. if (!refNode) { out = void 0; next = void 0; found = false; break; } type = refNode.$type; next = refNode; optimizedPath = ref[1].slice(0); } if (type) { break; } } // If there is a value, then we have great success, else, report an undefined. else { out = next; } curr = next; } if (depth < len && !expired) { // Unfortunately, if all that follows are nulls, then we have not shorted. for (i = depth; i < len; ++i) { if (simplePath[depth] !== null) { shouldShort = true; break; } } // if we should short or report value. Values are reported on nulls. if (shouldShort) { shorted = true; out = void 0; } else { out = next; } for (i = depth; i < len; ++i) { if (simplePath[i] !== null) { optimizedPath[optimizedPath.length] = simplePath[i]; } } } // promotes if not expired if (out && type) { if (isExpired(out)) { out = void 0; } else { promote(model._root, out); } } // if (out && out.$type === $error && !model._treatErrorsAsValues) { if (out && type === $error && !model._treatErrorsAsValues) { /* eslint-disable no-throw-literal */ throw { path: depth === len ? simplePath : simplePath.slice(0, depth), value: out.value }; /* eslint-enable no-throw-literal */ } else if (out && model._boxed) { out = Boolean(type) && !noClone ? clone(out) : out; } else if (!out && model._materialized) { out = {$type: $atom}; } else if (out) { out = out.value; } return { value: out, shorted: shorted, optimizedPath: optimizedPath, found: found }; }; },{"108":108,"109":109,"110":110,"15":15,"29":29,"30":30,"40":40}],22:[function(require,module,exports){ var getValueSync = require(21); module.exports = function _getVersion(model, path) { // ultra fast clone for boxed values. var gen = getValueSync({ _boxed: true, _root: model._root, _treatErrorsAsValues: model._treatErrorsAsValues }, path, true).value; var version = gen && gen.$_version; return (version == null) ? -1 : version; }; },{"21":21}],23:[function(require,module,exports){ var get = require(16); var walkPath = require(32); var getWithPathsAsPathMap = get(walkPath, false); var getWithPathsAsJSONGraph = get(walkPath, true); module.exports = { getValueSync: require(21), getBoundValue: require(17), getWithPathsAsPathMap: getWithPathsAsPathMap, getWithPathsAsJSONGraph: getWithPathsAsJSONGraph }; },{"16":16,"17":17,"21":21,"32":32}],24:[function(require,module,exports){ var promote = require(40); var clone = require(29); module.exports = function onError(model, node, depth, requestedPath, outerResults) { var value = node.value; if (!outerResults.errors) { outerResults.errors = []; } if (model._boxed) { value = clone(node); } outerResults.errors.push({ path: requestedPath.slice(0, depth), value: value }); promote(model._root, node); }; },{"29":29,"40":40}],25:[function(require,module,exports){ module.exports = function onMissing(model, path, depth, outerResults, requestedPath, optimizedPath, optimizedLength) { var pathSlice; if (!outerResults.requestedMissingPaths) { outerResults.requestedMissingPaths = []; outerResults.optimizedMissingPaths = []; } if (depth < path.length) { // If part of path has not been traversed, we need to ensure that there // are no empty paths (range(1, 0) or empyt array) var isEmpty = false; for (var i = depth; i < path.length && !isEmpty; ++i) { if (isEmptyAtom(path[i])) { return; } } pathSlice = path.slice(depth); } else { pathSlice = []; } concatAndInsertMissing(model, pathSlice, depth, requestedPath, optimizedPath, optimizedLength, outerResults); }; function concatAndInsertMissing(model, remainingPath, depth, requestedPath, optimizedPath, optimizedLength, results) { var requested = requestedPath.slice(0, depth); Array.prototype.push.apply(requested, remainingPath); results.requestedMissingPaths[results.requestedMissingPaths.length] = requested; var optimized = optimizedPath.slice(0, optimizedLength); Array.prototype.push.apply(optimized, remainingPath); results.optimizedMissingPaths[results.optimizedMissingPaths.length] = optimized; } function isEmptyAtom(atom) { if (atom === null || typeof atom !== "object") { return false; } var isArray = Array.isArray(atom); if (isArray && atom.length) { return false; } // Empty array else if (isArray) { return true; } var from = atom.from; var to = atom.to; if (from === undefined || from <= to) { return false; } return true; } },{}],26:[function(require,module,exports){ var promote = require(40); var clone = require(29); var $ref = require(110); var $atom = require(108); var $error = require(109); module.exports = function onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG) { // Promote first. Even if no output is produced we should still promote. if (node) { promote(model._root, node); } // Preload if (!seed) { return; } var i, len, k, key, curr, prev = null, prevK; var materialized = false, valueNode, nodeType = node && node.$type, nodeValue = node && node.value; if (nodeValue === undefined) { materialized = model._materialized; } // materialized if (materialized) { valueNode = {$type: $atom}; } // Boxed Mode will clone the node. else if (model._boxed) { valueNode = clone(node); } // We don't want to emit references in json output else if (!isJSONG && nodeType === $ref) { valueNode = undefined; } // JSONG always clones the node. else if (nodeType === $ref || nodeType === $error) { if (isJSONG) { valueNode = clone(node); } else { valueNode = nodeValue; } } else if (isJSONG) { var isObject = nodeValue && typeof nodeValue === "object"; var isUserCreatedNode = !node || !node.$_modelCreated; if (isObject || isUserCreatedNode) { valueNode = clone(node); } else { valueNode = nodeValue; } } else if (node && nodeType === undefined && nodeValue === undefined) { // Include an empty value for branch nodes valueNode = {}; } else { valueNode = nodeValue; } var hasValues = false; if (isJSONG) { curr = seed.jsonGraph; if (!curr) { hasValues = true; curr = seed.jsonGraph = {}; seed.paths = []; } for (i = 0, len = optimizedLength - 1; i < len; i++) { key = optimizedPath[i]; if (!curr[key]) { hasValues = true; curr[key] = {}; } curr = curr[key]; } // assign the last key = optimizedPath[i]; // TODO: Special case? do string comparisons make big difference? curr[key] = materialized ? {$type: $atom} : valueNode; if (requestedPath) { seed.paths.push(requestedPath.slice(0, depth)); } } // The output is pathMap and the depth is 0. It is just a // value report it as the found JSON else if (depth === 0) { hasValues = true; seed.json = valueNode; } // The output is pathMap but we need to build the pathMap before // reporting the value. else { curr = seed.json; if (!curr) { hasValues = true; curr = seed.json = {}; } for (i = 0; i < depth - 1; i++) { k = requestedPath[i]; // The branch info is already generated output from the walk algo // with the required __path information on it. if (!curr[k]) { hasValues = true; curr[k] = branchInfo[i]; } prev = curr; prevK = k; curr = curr[k]; } k = requestedPath[i]; if (valueNode !== undefined) { if (k != null) { hasValues = true; if (!curr[k]) { curr[k] = valueNode; } } else { // We are protected from reaching here when depth is 1 and prev is // undefined by the InvalidModelError and NullInPathError checks. prev[prevK] = valueNode; } } } if (outerResults) { outerResults.hasValues = hasValues; } }; },{"108":108,"109":109,"110":110,"29":29,"40":40}],27:[function(require,module,exports){ var isExpired = require(30); var $error = require(109); var onError = require(24); var onValue = require(26); var onMissing = require(25); var isMaterialized = require(31); var expireNode = require(75); var currentCacheVersion = require(74); /** * When we land on a valueType (or nothing) then we need to report it out to * the outerResults through errors, missing, or values. * * @private */ module.exports = function onValueType( model, node, path, depth, seed, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG, fromReference) { var currType = node && node.$type; // There are is nothing here, ether report value, or report the value // that is missing. If there is no type then report the missing value. if (!node || !currType) { var materialized = isMaterialized(model); if (materialized || !isJSONG) { onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); } if (!materialized) { onMissing(model, path, depth, outerResults, requestedPath, optimizedPath, optimizedLength); } return; } // If there are expired value, then report it as missing else if (isExpired(node) && !(node.$_version === currentCacheVersion.getVersion() && node.$expires === 0)) { if (!node.$_invalidated) { expireNode(node, model._root.expired, model._root); } onMissing(model, path, depth, outerResults, requestedPath, optimizedPath, optimizedLength); } // If there is an error, then report it as a value if else if (currType === $error) { if (fromReference) { requestedPath[depth] = null; depth += 1; } if (isJSONG || model._treatErrorsAsValues) { onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); } else { onValue(model, undefined, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); onError(model, node, depth, requestedPath, outerResults); } } // Report the value else { if (fromReference) { requestedPath[depth] = null; depth += 1; } onValue(model, node, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); } }; },{"109":109,"24":24,"25":25,"26":26,"30":30,"31":31,"74":74,"75":75}],28:[function(require,module,exports){ var pathSyntax = require(124); var getValueSync = require(21); module.exports = function _getValueSync(pathArg) { var path = pathSyntax.fromPath(pathArg); if (Array.isArray(path) === false) { throw new Error("Model#_getValueSync must be called with an Array path."); } if (this._path.length) { path = this._path.concat(path); } this._syncCheck("getValueSync"); return getValueSync(this, path).value; }; },{"124":124,"21":21}],29:[function(require,module,exports){ // Copies the node var privatePrefix = require(34); module.exports = function clone(node) { if (node === undefined) { return node; } var outValue = {}; for (var k in node) { if (k.lastIndexOf(privatePrefix, 0) === 0) { continue; } outValue[k] = node[k]; } return outValue; }; },{"34":34}],30:[function(require,module,exports){ module.exports = require(84); },{"84":84}],31:[function(require,module,exports){ module.exports = function isMaterialized(model) { return model._materialized && !model._source; }; },{}],32:[function(require,module,exports){ var followReference = require(15); var onValueType = require(27); var onValue = require(26); var isExpired = require(30); var iterateKeySet = require(136).iterateKeySet; var $ref = require(110); var promote = require(40); module.exports = function walkPath(model, root, curr, path, depth, seed, outerResults, branchInfo, requestedPath, optimizedPathArg, optimizedLength, isJSONG, fromReferenceArg, referenceContainerArg) { var fromReference = fromReferenceArg; var optimizedPath = optimizedPathArg; var referenceContainer = referenceContainerArg; // The walk is finished when: // - there is no value in the current cache position // - there is a JSONG leaf node in the current cache position // - we've reached the end of the path if (!curr || curr.$type || depth === path.length) { onValueType(model, curr, path, depth, seed, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG, fromReference); return; } var keySet = path[depth]; var isKeySet = keySet !== null && typeof keySet === "object"; var iteratorNote = false; var key = keySet; if (isKeySet) { iteratorNote = {}; key = iterateKeySet(keySet, iteratorNote); } var allowFromWhenceYouCame = model._allowFromWhenceYouCame; var optimizedLengthPlus1 = optimizedLength + 1; var nextDepth = depth + 1; var refPath; // loop over every key in the key set do { if (key == null) { // Skip null/undefined/empty keysets in path and do not descend, // but capture the partial path in the result onValue(model, curr, seed, depth, outerResults, branchInfo, requestedPath, optimizedPath, optimizedLength, isJSONG); if (iteratorNote && !iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } continue; } fromReference = false; optimizedPath[optimizedLength] = key; requestedPath[depth] = key; var next = curr[key]; var nextOptimizedPath = optimizedPath; var nextOptimizedLength = optimizedLengthPlus1; // If there is the next position we need to consider references. if (next) { var nType = next.$type; var value = nType && next.value || next; // If next is a reference follow it. If we are in JSONG mode, // report that value into the seed without passing the requested // path. If a requested path is passed to onValueType then it // will add that path to the JSONGraph envelope under `paths` if (nextDepth < path.length && nType && nType === $ref && !isExpired(next)) { // promote the node so that the references don't get cleaned up. promote(model._root, next); if (isJSONG) { onValue(model, next, seed, nextDepth, outerResults, null, null, optimizedPath, nextOptimizedLength, isJSONG); } var ref = followReference(model, root, root, next, value, seed, isJSONG); fromReference = true; next = ref[0]; refPath = ref[1]; referenceContainer = ref[2]; nextOptimizedPath = refPath.slice(); nextOptimizedLength = refPath.length; } // The next can be set to undefined by following a reference that // does not exist. if (next) { var obj; // There was a reference container. if (referenceContainer && allowFromWhenceYouCame) { obj = { // eslint-disable-next-line camelcase $__path: next.$_absolutePath, // eslint-disable-next-line camelcase $__refPath: referenceContainer.value, // eslint-disable-next-line camelcase $__toReference: referenceContainer.$_absolutePath }; } // There is no reference container meaning this request was // neither from a model and/or the first n (depth) keys do not // contain references. else { obj = { // eslint-disable-next-line camelcase $__path: next.$_absolutePath }; } branchInfo[depth] = obj; } } // Recurse to the next level. walkPath(model, root, next, path, nextDepth, seed, outerResults, branchInfo, requestedPath, nextOptimizedPath, nextOptimizedLength, isJSONG, fromReference, referenceContainer); // If the iteratorNote is not done, get the next key. if (iteratorNote && !iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (iteratorNote && !iteratorNote.done); }; },{"110":110,"136":136,"15":15,"26":26,"27":27,"30":30,"40":40}],33:[function(require,module,exports){ "use strict"; function falcor(opts) { return new falcor.Model(opts); } /** * A filtering method for keys from a falcor json response. The only gotcha * to this method is when the incoming json is undefined, then undefined will * be returned. * * @public * @param {Object} json - The json response from a falcor model. * @returns {Array} - the keys that are in the model response minus the deref * _private_ meta data. */ falcor.keys = function getJSONKeys(json) { if (!json) { return undefined; } return Object. keys(json). filter(function(key) { return key !== "$__path"; }); }; module.exports = falcor; falcor.Model = require(2); },{"2":2}],34:[function(require,module,exports){ var reservedPrefix = require(36); module.exports = reservedPrefix + "_"; },{"36":36}],35:[function(require,module,exports){ module.exports = require(34) + "ref"; },{"34":34}],36:[function(require,module,exports){ module.exports = "$"; },{}],37:[function(require,module,exports){ var createHardlink = require(73); var __prefix = require(36); var $ref = require(110); var getBoundValue = require(17); var promote = require(40); var getSize = require(77); var hasOwn = require(80); var isObject = require(89); var isExpired = require(84); var isFunction = require(85); var isPrimitive = require(91); var expireNode = require(75); var incrementVersion = require(81); var updateNodeAncestors = require(104); var removeNodeAndDescendants = require(98); /** * Sets a list of PathMaps into a JSON Graph. * @function * @param {Object} model - the Model for which to insert the PathMaps. * @param {Array.} pathMapEnvelopes - the a list of @PathMapEnvelopes to set. */ module.exports = function invalidatePathMaps(model, pathMapEnvelopes) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; var parent = node.$_parent || cache; var initialVersion = cache.$_version; var pathMapIndex = -1; var pathMapCount = pathMapEnvelopes.length; while (++pathMapIndex < pathMapCount) { var pathMapEnvelope = pathMapEnvelopes[pathMapIndex]; invalidatePathMap(pathMapEnvelope.json, cache, parent, node, version, expired, lru); } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } }; function invalidatePathMap(pathMap, root, parent, node, version, expired, lru) { if (isPrimitive(pathMap) || pathMap.$type) { return; } for (var key in pathMap) { if (key[0] !== __prefix && hasOwn(pathMap, key)) { var child = pathMap[key]; var branch = isObject(child) && !child.$type; var results = invalidateNode(root, parent, node, key, branch, expired, lru); var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { invalidatePathMap(child, root, nextParent, nextNode, version, expired, lru); } else if (removeNodeAndDescendants(nextNode, nextParent, key, lru)) { updateNodeAncestors(nextParent, getSize(nextNode), lru, version); } } } } } function invalidateReference(root, node, expired, lru) { if (isExpired(node)) { expireNode(node, expired, lru); return [undefined, root]; } promote(lru, node); var container = node; var reference = node.value; var parent = root; node = node.$_context; if (node != null) { parent = node.$_parent || root; } else { var index = 0; var count = reference.length - 1; parent = node = root; do { var key = reference[index]; var branch = index < count; var results = invalidateNode(root, parent, node, key, branch, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; } while (index++ < count); if (container.$_context !== node) { createHardlink(container, node); } } return [node, parent]; } function invalidateNode(root, parent, node, key, branch, expired, lru) { var type = node.$type; while (type === $ref) { var results = invalidateReference(root, node, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node && node.$type; } if (type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new Error("`null` is not allowed in branch key positions."); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } return [node, parent]; } },{"104":104,"110":110,"17":17,"36":36,"40":40,"73":73,"75":75,"77":77,"80":80,"81":81,"84":84,"85":85,"89":89,"91":91,"98":98}],38:[function(require,module,exports){ var __ref = require(35); var $ref = require(110); var getBoundValue = require(17); var promote = require(40); var getSize = require(77); var isExpired = require(84); var isFunction = require(85); var isPrimitive = require(91); var expireNode = require(75); var iterateKeySet = require(136).iterateKeySet; var incrementVersion = require(81); var updateNodeAncestors = require(104); var removeNodeAndDescendants = require(98); /** * Invalidates a list of Paths in a JSON Graph. * @function * @param {Object} model - the Model for which to insert the PathValues. * @param {Array.} paths - the PathValues to set. */ module.exports = function invalidatePathSets(model, paths) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; // eslint-disable-next-line camelcase var parent = node.$_parent || cache; // eslint-disable-next-line camelcase var initialVersion = cache.$_version; var pathIndex = -1; var pathCount = paths.length; while (++pathIndex < pathCount) { var path = paths[pathIndex]; invalidatePathSet(path, 0, cache, parent, node, version, expired, lru); } // eslint-disable-next-line camelcase var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } }; function invalidatePathSet( path, depth, root, parent, node, version, expired, lru) { var note = {}; var branch = depth < path.length - 1; var keySet = path[depth]; var key = iterateKeySet(keySet, note); do { var results = invalidateNode(root, parent, node, key, branch, expired, lru); var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { invalidatePathSet( path, depth + 1, root, nextParent, nextNode, version, expired, lru ); } else if (removeNodeAndDescendants(nextNode, nextParent, key, lru, undefined)) { updateNodeAncestors(nextParent, getSize(nextNode), lru, version); } } key = iterateKeySet(keySet, note); } while (!note.done); } function invalidateReference(root, node, expired, lru) { if (isExpired(node)) { expireNode(node, expired, lru); return [undefined, root]; } promote(lru, node); var container = node; var reference = node.value; var parent = root; // eslint-disable-next-line camelcase node = node.$_context; if (node != null) { // eslint-disable-next-line camelcase parent = node.$_parent || root; } else { var index = 0; var count = reference.length - 1; parent = node = root; do { var key = reference[index]; var branch = index < count; var results = invalidateNode(root, parent, node, key, branch, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; } while (index++ < count); // eslint-disable-next-line camelcase if (container.$_context !== node) { // eslint-disable-next-line camelcase var backRefs = node.$_refsLength || 0; // eslint-disable-next-line camelcase node.$_refsLength = backRefs + 1; node[__ref + backRefs] = container; // eslint-disable-next-line camelcase container.$_context = node; // eslint-disable-next-line camelcase container.$_refIndex = backRefs; } } return [node, parent]; } function invalidateNode(root, parent, node, key, branch, expired, lru) { var type = node.$type; while (type === $ref) { var results = invalidateReference(root, node, expired, lru); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node.$type; } if (type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new Error("`null` is not allowed in branch key positions."); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } return [node, parent]; } },{"104":104,"110":110,"136":136,"17":17,"35":35,"40":40,"75":75,"77":77,"81":81,"84":84,"85":85,"91":91,"98":98}],39:[function(require,module,exports){ var removeNode = require(97); var updateNodeAncestors = require(104); module.exports = function collect(lru, expired, totalArg, max, ratioArg, version) { var total = totalArg; var ratio = ratioArg; if (typeof ratio !== "number") { ratio = 0.75; } var shouldUpdate = typeof version === "number"; var targetSize = max * ratio; var parent, node, size; node = expired.pop(); while (node) { size = node.$size || 0; total -= size; if (shouldUpdate === true) { updateNodeAncestors(node, size, lru, version); // eslint-disable-next-line camelcase } else if (parent = node.$_parent) { // eslint-disable-line no-cond-assign // eslint-disable-next-line camelcase removeNode(node, parent, node.$_key, lru); } node = expired.pop(); } if (total >= max) { // eslint-disable-next-line camelcase var prev = lru.$_tail; node = prev; while ((total >= targetSize) && node) { // eslint-disable-next-line camelcase prev = prev.$_prev; size = node.$size || 0; total -= size; if (shouldUpdate === true) { updateNodeAncestors(node, size, lru, version); } node = prev; } // eslint-disable-next-line camelcase lru.$_tail = lru.$_prev = node; if (node == null) { // eslint-disable-next-line camelcase lru.$_head = lru.$_next = undefined; } else { // eslint-disable-next-line camelcase node.$_next = undefined; } } }; },{"104":104,"97":97}],40:[function(require,module,exports){ var EXPIRES_NEVER = require(111); // [H] -> Next -> ... -> [T] // [T] -> Prev -> ... -> [H] module.exports = function lruPromote(root, object) { // Never promote node.$expires === 1. They cannot expire. if (object.$expires === EXPIRES_NEVER) { return; } // eslint-disable-next-line camelcase var head = root.$_head; // Nothing is in the cache. if (!head) { // eslint-disable-next-line camelcase root.$_head = root.$_tail = object; return; } if (head === object) { return; } // The item always exist in the cache since to get anything in the // cache it first must go through set. // eslint-disable-next-line camelcase var prev = object.$_prev; // eslint-disable-next-line camelcase var next = object.$_next; if (next) { // eslint-disable-next-line camelcase next.$_prev = prev; } if (prev) { // eslint-disable-next-line camelcase prev.$_next = next; } // eslint-disable-next-line camelcase object.$_prev = undefined; // Insert into head position // eslint-disable-next-line camelcase root.$_head = object; // eslint-disable-next-line camelcase object.$_next = head; // eslint-disable-next-line camelcase head.$_prev = object; // If the item we promoted was the tail, then set prev to tail. // eslint-disable-next-line camelcase if (object === root.$_tail) { // eslint-disable-next-line camelcase root.$_tail = prev; } }; },{"111":111}],41:[function(require,module,exports){ module.exports = function lruSplice(root, object) { // Its in the cache. Splice out. // eslint-disable-next-line camelcase var prev = object.$_prev; // eslint-disable-next-line camelcase var next = object.$_next; if (next) { // eslint-disable-next-line camelcase next.$_prev = prev; } if (prev) { // eslint-disable-next-line camelcase prev.$_next = next; } // eslint-disable-next-line camelcase object.$_prev = object.$_next = undefined; // eslint-disable-next-line camelcase if (object === root.$_head) { // eslint-disable-next-line camelcase root.$_head = next; } // eslint-disable-next-line camelcase if (object === root.$_tail) { // eslint-disable-next-line camelcase root.$_tail = prev; } }; },{}],42:[function(require,module,exports){ var complement = require(45); var flushGetRequest = require(46); var incrementVersion = require(81); var currentCacheVersion = require(74); var REQUEST_ID = 0; var GetRequestType = require(44).GetRequest; var setJSONGraphs = require(66); var setPathValues = require(68); var $error = require(109); var emptyArray = []; var InvalidSourceError = require(11); /** * Creates a new GetRequest. This GetRequest takes a scheduler and * the request queue. Once the scheduler fires, all batched requests * will be sent to the server. Upon request completion, the data is * merged back into the cache and all callbacks are notified. * * @param {Scheduler} scheduler - * @param {RequestQueueV2} requestQueue - * @param {number} attemptCount */ var GetRequestV2 = function(scheduler, requestQueue, attemptCount) { this.sent = false; this.scheduled = false; this.requestQueue = requestQueue; this.id = ++REQUEST_ID; this.type = GetRequestType; this._scheduler = scheduler; this._attemptCount = attemptCount; this._pathMap = {}; this._optimizedPaths = []; this._requestedPaths = []; this._callbacks = []; this._count = 0; this._disposable = null; this._collapsed = null; this._disposed = false; }; GetRequestV2.prototype = { /** * batches the paths that are passed in. Once the request is complete, * all callbacks will be called and the request will be removed from * parent queue. * @param {Array} requestedPaths - * @param {Array} optimizedPaths - * @param {Function} callback - */ batch: function(requestedPaths, optimizedPaths, callback) { var self = this; var batchedOptPathSets = self._optimizedPaths; var batchedReqPathSets = self._requestedPaths; var batchedCallbacks = self._callbacks; var batchIx = batchedOptPathSets.length; // If its not sent, simply add it to the requested paths // and callbacks. batchedOptPathSets[batchIx] = optimizedPaths; batchedReqPathSets[batchIx] = requestedPaths; batchedCallbacks[batchIx] = callback; ++self._count; // If it has not been scheduled, then schedule the action if (!self.scheduled) { self.scheduled = true; var flushedDisposable; var scheduleDisposable = self._scheduler.schedule(function() { flushedDisposable = flushGetRequest(self, batchedOptPathSets, function(err, data) { var i, fn, len; var model = self.requestQueue.model; self.requestQueue.removeRequest(self); self._disposed = true; if (model._treatDataSourceErrorsAsJSONGraphErrors ? err instanceof InvalidSourceError : !!err) { for (i = 0, len = batchedCallbacks.length; i < len; ++i) { fn = batchedCallbacks[i]; if (fn) { fn(err); } } return; } // If there is at least one callback remaining, then // callback the callbacks. if (self._count) { // currentVersion will get added to each inserted // node as node.$_version inside of self._merge. // // atom values just downloaded with $expires: 0 // (now-expired) will get assigned $_version equal // to currentVersion, and checkCacheAndReport will // later consider those nodes to not have expired // for the duration of current event loop tick // // we unset currentCacheVersion after all callbacks // have been called, to ensure that only these // particular callbacks and any synchronous model.get // callbacks inside of these, get the now-expired // values var currentVersion = incrementVersion.getCurrentVersion(); currentCacheVersion.setVersion(currentVersion); var mergeContext = { hasInvalidatedResult: false }; var pathsErr = model._useServerPaths && data && data.paths === undefined ? new Error("Server responses must include a 'paths' field when Model._useServerPaths === true") : undefined; if (!pathsErr) { self._merge(batchedReqPathSets, err, data, mergeContext); } // Call the callbacks. The first one inserts all // the data so that the rest do not have consider // if their data is present or not. for (i = 0, len = batchedCallbacks.length; i < len; ++i) { fn = batchedCallbacks[i]; if (fn) { fn(pathsErr || err, data, mergeContext.hasInvalidatedResult); } } currentCacheVersion.setVersion(null); } }); self._disposable = flushedDisposable; }); // If the scheduler is sync then `flushedDisposable` will be // defined, and we want to use it, because that's what aborts an // in-flight XHR request, for example. // But if the scheduler is async, then `flushedDisposable` won't be // defined yet, and so we must use the scheduler's disposable until // `flushedDisposable` is defined. Since we want to still use // `flushedDisposable` once it is defined (to be able to abort in- // flight XHR requests), hence the reassignment of `_disposable` // above. self._disposable = flushedDisposable || scheduleDisposable; } // Disposes this batched request. This does not mean that the // entire request has been disposed, but just the local one, if all // requests are disposed, then the outer disposable will be removed. return createDisposable(self, batchIx); }, /** * Attempts to add paths to the outgoing request. If there are added * paths then the request callback will be added to the callback list. * Handles adding partial paths as well * * @returns {Array} - whether new requested paths were inserted in this * request, the remaining paths that could not be added, * and disposable for the inserted requested paths. */ add: function(requested, optimized, callback) { // uses the length tree complement calculator. var self = this; var complementResult = complement(requested, optimized, self._pathMap); var inserted = false; var disposable = false; // If we found an intersection, then just add new callback // as one of the dependents of that request if (complementResult.intersection.length) { inserted = true; var batchIx = self._callbacks.length; self._callbacks[batchIx] = callback; self._requestedPaths[batchIx] = complementResult.intersection; self._optimizedPaths[batchIx] = []; ++self._count; disposable = createDisposable(self, batchIx); } return [inserted, complementResult.requestedComplement, complementResult.optimizedComplement, disposable]; }, /** * merges the response into the model"s cache. */ _merge: function(requested, err, data, mergeContext) { var self = this; var model = self.requestQueue.model; var modelRoot = model._root; var errorSelector = modelRoot.errorSelector; var comparator = modelRoot.comparator; var boundPath = model._path; model._path = emptyArray; // flatten all the requested paths, adds them to the var nextPaths = model._useServerPaths ? data.paths : flattenRequestedPaths(requested); // Insert errors in every requested position. if (err && model._treatDataSourceErrorsAsJSONGraphErrors) { var error = err; // Converts errors to objects, a more friendly storage // of errors. if (error instanceof Error) { error = { message: error.message }; } // Not all errors are value $types. if (!error.$type) { error = { $type: $error, value: error }; } var pathValues = nextPaths.map(function(x) { return { path: x, value: error }; }); setPathValues(model, pathValues, null, errorSelector, comparator, mergeContext); } // Insert the jsonGraph from the dataSource. else { setJSONGraphs(model, [{ paths: nextPaths, jsonGraph: data.jsonGraph }], null, errorSelector, comparator, mergeContext); } // return the model"s boundPath model._path = boundPath; } }; // Creates a more efficient closure of the things that are // needed. So the request and the batch index. Also prevents code // duplication. function createDisposable(request, batchIx) { var disposed = false; return function() { if (disposed || request._disposed) { return; } disposed = true; request._callbacks[batchIx] = null; request._optimizedPaths[batchIx] = []; request._requestedPaths[batchIx] = []; // If there are no more requests, then dispose all of the request. var count = --request._count; var disposable = request._disposable; if (count === 0) { // looking for unsubscribe here to support more data sources (Rx) if (disposable.unsubscribe) { disposable.unsubscribe(); } else { disposable.dispose(); } request.requestQueue.removeRequest(request); } }; } function flattenRequestedPaths(requested) { var out = []; var outLen = -1; for (var i = 0, len = requested.length; i < len; ++i) { var paths = requested[i]; for (var j = 0, innerLen = paths.length; j < innerLen; ++j) { out[++outLen] = paths[j]; } } return out; } module.exports = GetRequestV2; },{"109":109,"11":11,"44":44,"45":45,"46":46,"66":66,"68":68,"74":74,"81":81}],43:[function(require,module,exports){ var RequestTypes = require(44); var sendSetRequest = require(47); var GetRequest = require(42); var falcorPathUtils = require(136); /** * The request queue is responsible for queuing the operations to * the model"s dataSource. * * @param {Model} model - * @param {Scheduler} scheduler - */ function RequestQueueV2(model, scheduler) { this.model = model; this.scheduler = scheduler; this.requests = this._requests = []; } RequestQueueV2.prototype = { /** * Sets the scheduler, but will not affect any current requests. */ setScheduler: function(scheduler) { this.scheduler = scheduler; }, /** * performs a set against the dataSource. Sets, though are not batched * currently could be batched potentially in the future. Since no batching * is required the setRequest action is simplified significantly. * * @param {JSONGraphEnvelope} jsonGraph - * @param {number} attemptCount * @param {Function} cb */ set: function(jsonGraph, attemptCount, cb) { if (this.model._enablePathCollapse) { jsonGraph.paths = falcorPathUtils.collapse(jsonGraph.paths); } if (cb === undefined) { cb = attemptCount; attemptCount = undefined; } return sendSetRequest(jsonGraph, this.model, attemptCount, cb); }, /** * Creates a get request to the dataSource. Depending on the current * scheduler is how the getRequest will be flushed. * @param {Array} requestedPaths - * @param {Array} optimizedPaths - * @param {number} attemptCount * @param {Function} cb - */ get: function(requestedPaths, optimizedPaths, attemptCount, cb) { var self = this; var disposables = []; var count = 0; var requests = self._requests; var i, len; var oRemainingPaths = optimizedPaths; var rRemainingPaths = requestedPaths; var disposed = false; var request; if (cb === undefined) { cb = attemptCount; attemptCount = undefined; } for (i = 0, len = requests.length; i < len; ++i) { request = requests[i]; if (request.type !== RequestTypes.GetRequest) { continue; } // The request has been sent, attempt to jump on the request // if possible. if (request.sent) { if (this.model._enableRequestDeduplication) { var results = request.add(rRemainingPaths, oRemainingPaths, refCountCallback); // Checks to see if the results were successfully inserted // into the outgoing results. Then our paths will be reduced // to the complement. if (results[0]) { rRemainingPaths = results[1]; oRemainingPaths = results[2]; disposables[disposables.length] = results[3]; ++count; // If there are no more remaining paths then exit the loop. if (!oRemainingPaths.length) { break; } } } } // If there is an unsent request, then we can batch and leave. else { request.batch(rRemainingPaths, oRemainingPaths, refCountCallback); oRemainingPaths = null; rRemainingPaths = null; ++count; break; } } // After going through all the available requests if there are more // paths to process then a new request must be made. if (oRemainingPaths && oRemainingPaths.length) { request = new GetRequest(self.scheduler, self, attemptCount); requests[requests.length] = request; ++count; var disposable = request.batch(rRemainingPaths, oRemainingPaths, refCountCallback); disposables[disposables.length] = disposable; } // This is a simple refCount callback. function refCountCallback(err, data, hasInvalidatedResult) { if (disposed) { return; } --count; // If the count becomes 0, then its time to notify the // listener that the request is done. if (count === 0) { cb(err, data, hasInvalidatedResult); } } // When disposing the request all of the outbound requests will be // disposed of. return function() { if (disposed || count === 0) { return; } disposed = true; var length = disposables.length; for (var idx = 0; idx < length; ++idx) { disposables[idx](); } }; }, /** * Removes the request from the request queue. */ removeRequest: function(request) { var requests = this._requests; var i = requests.length; while (--i >= 0) { if (requests[i].id === request.id) { requests.splice(i, 1); break; } } } }; module.exports = RequestQueueV2; },{"136":136,"42":42,"44":44,"47":47}],44:[function(require,module,exports){ module.exports = { GetRequest: "GET" }; },{}],45:[function(require,module,exports){ var iterateKeySet = require(136).iterateKeySet; /** * Calculates what paths in requested path sets can be deduplicated based on an existing optimized path tree. * * For path sets with ranges or key sets, if some expanded paths can be found in the path tree, only matching paths are * returned as intersection. The non-matching expanded paths are returned as complement. * * The function returns an object consisting of: * - intersection: requested paths that were matched to the path tree * - optimizedComplement: optimized paths that were not found in the path tree * - requestedComplement: requested paths for the optimized paths that were not found in the path tree */ module.exports = function complement(requested, optimized, tree) { var optimizedComplement = []; var requestedComplement = []; var intersection = []; var i, iLen; for (i = 0, iLen = optimized.length; i < iLen; ++i) { var oPath = optimized[i]; var rPath = requested[i]; var subTree = tree[oPath.length]; var intersectionData = findPartialIntersections(rPath, oPath, subTree); Array.prototype.push.apply(intersection, intersectionData[0]); Array.prototype.push.apply(optimizedComplement, intersectionData[1]); Array.prototype.push.apply(requestedComplement, intersectionData[2]); } return { intersection: intersection, optimizedComplement: optimizedComplement, requestedComplement: requestedComplement }; }; /** * Recursive function to calculate intersection and complement paths in 2 given pathsets at a given depth. * * Parameters: * - requestedPath: full requested path set (can include ranges) * - optimizedPath: corresponding optimized path (can include ranges) * - requestTree: path tree for in-flight request, against which to dedupe * * Returns a 3-tuple consisting of * - the intersection of requested paths with requestTree * - the complement of optimized paths with requestTree * - the complement of corresponding requested paths with requestTree * * Example scenario: * - requestedPath: ['lolomo', 0, 0, 'tags', { from: 0, to: 2 }] * - optimizedPath: ['videosById', 11, 'tags', { from: 0, to: 2 }] * - requestTree: { videosById: 11: { tags: { 0: null, 1: null }}} * * This returns: * [ * [['lolomo', 0, 0, 'tags', 0], ['lolomo', 0, 0, 'tags', 1]], * [['videosById', 11, 'tags', 2]], * [['lolomo', 0, 0, 'tags', 2]] * ] * */ function findPartialIntersections(requestedPath, optimizedPath, requestTree) { var depthDiff = requestedPath.length - optimizedPath.length; var i; // Descend into the request path tree for the optimized-path prefix (when the optimized path is longer than the // requested path) for (i = 0; requestTree && i < -depthDiff; i++) { requestTree = requestTree[optimizedPath[i]]; } // There is no matching path in the request path tree, thus no candidates for deduplication if (!requestTree) { return [[], [optimizedPath], [requestedPath]]; } if (depthDiff === 0) { return recurse(requestedPath, optimizedPath, requestTree, 0, [], []); } else if (depthDiff > 0) { return recurse(requestedPath, optimizedPath, requestTree, 0, requestedPath.slice(0, depthDiff), []); } else { return recurse(requestedPath, optimizedPath, requestTree, -depthDiff, [], optimizedPath.slice(0, -depthDiff)); } } function recurse(requestedPath, optimizedPath, currentTree, depth, rCurrentPath, oCurrentPath) { var depthDiff = requestedPath.length - optimizedPath.length; var intersections = []; var rComplementPaths = []; var oComplementPaths = []; var oPathLen = optimizedPath.length; // Loop over the optimized path, looking for deduplication opportunities for (; depth < oPathLen; ++depth) { var key = optimizedPath[depth]; var keyType = typeof key; if (key && keyType === "object") { // If a range key is found, start an inner loop to iterate over all keys in the range, and add // intersections and complements from each iteration separately. // // Range keys branch out this way, providing individual deduping opportunities for each inner key. var note = {}; var innerKey = iterateKeySet(key, note); while (!note.done) { var nextTree = currentTree[innerKey]; if (nextTree === undefined) { // If no next sub tree exists for an inner key, it's a dead-end and we can add this to // complement paths oComplementPaths[oComplementPaths.length] = arrayConcatSlice2( oCurrentPath, innerKey, optimizedPath, depth + 1 ); rComplementPaths[rComplementPaths.length] = arrayConcatSlice2( rCurrentPath, innerKey, requestedPath, depth + 1 + depthDiff ); } else if (depth === oPathLen - 1) { // Reaching the end of the optimized path means that we found the entire path in the path tree, // so add it to intersections intersections[intersections.length] = arrayConcatElement(rCurrentPath, innerKey); } else { // Otherwise keep trying to find further partial deduping opportunities in the remaining path var intersectionData = recurse( requestedPath, optimizedPath, nextTree, depth + 1, arrayConcatElement(rCurrentPath, innerKey), arrayConcatElement(oCurrentPath, innerKey) ); Array.prototype.push.apply(intersections, intersectionData[0]); Array.prototype.push.apply(oComplementPaths, intersectionData[1]); Array.prototype.push.apply(rComplementPaths, intersectionData[2]); } innerKey = iterateKeySet(key, note); } // The remainder of the path was handled by the recursive call, terminate the loop break; } else { // For simple keys, we don't need to branch out. Loop over `depth` instead of iterating over a range. currentTree = currentTree[key]; oCurrentPath[oCurrentPath.length] = optimizedPath[depth]; rCurrentPath[rCurrentPath.length] = requestedPath[depth + depthDiff]; if (currentTree === undefined) { // The path was not found in the tree, add this to complements oComplementPaths[oComplementPaths.length] = arrayConcatSlice( oCurrentPath, optimizedPath, depth + 1 ); rComplementPaths[rComplementPaths.length] = arrayConcatSlice( rCurrentPath, requestedPath, depth + depthDiff + 1 ); break; } else if (depth === oPathLen - 1) { // The end of optimized path was reached, add to intersections intersections[intersections.length] = rCurrentPath; } } } // Return accumulated intersection and complement paths return [intersections, oComplementPaths, rComplementPaths]; } // Exported for unit testing. module.exports.__test = { findPartialIntersections: findPartialIntersections }; /** * Create a new array consisting of a1 + a subset of a2. Avoids allocating an extra array by calling `slice` on a2. */ function arrayConcatSlice(a1, a2, start) { var result = a1.slice(); var l1 = result.length; var length = a2.length - start; result.length = l1 + length; for (var i = 0; i < length; ++i) { result[l1 + i] = a2[start + i]; } return result; } /** * Create a new array consisting of a1 + a2 + a subset of a3. Avoids allocating an extra array by calling `slice` on a3. */ function arrayConcatSlice2(a1, a2, a3, start) { var result = a1.concat(a2); var l1 = result.length; var length = a3.length - start; result.length = l1 + length; for (var i = 0; i < length; ++i) { result[l1 + i] = a3[start + i]; } return result; } /** * Create a new array consistent of a1 plus an additional element. Avoids the unnecessary array allocation when using `a1.concat([element])`. */ function arrayConcatElement(a1, element) { var result = a1.slice(); result.push(element); return result; } },{"136":136}],46:[function(require,module,exports){ var pathUtils = require(136); var toTree = pathUtils.toTree; var toPaths = pathUtils.toPaths; var InvalidSourceError = require(11); /** * Flushes the current set of requests. This will send the paths to the dataSource. * The results of the dataSource will be sent to callback which should perform the zip of all callbacks. * * @param {GetRequest} request - GetRequestV2 to be flushed to the DataSource * @param {Array} pathSetArrayBatch - Array of Arrays of path sets * @param {Function} callback - * @private */ module.exports = function flushGetRequest(request, pathSetArrayBatch, callback) { if (request._count === 0) { request.requestQueue.removeRequest(request); return null; } request.sent = true; request.scheduled = false; var requestPaths; var model = request.requestQueue.model; if (model._enablePathCollapse || model._enableRequestDeduplication) { // Note on the if-condition: request deduplication uses request._pathMap, // so we need to populate that field if the feature is enabled. // TODO: Move this to the collapse algorithm, // TODO: we should have a collapse that returns the paths and // TODO: the trees. // Take all the paths and add them to the pathMap by length. // Since its a list of paths var pathMap = request._pathMap; var listIdx = 0, listLen = pathSetArrayBatch.length; for (; listIdx < listLen; ++listIdx) { var paths = pathSetArrayBatch[listIdx]; for (var j = 0, pathLen = paths.length; j < pathLen; ++j) { var pathSet = paths[j]; var len = pathSet.length; if (!pathMap[len]) { pathMap[len] = [pathSet]; } else { var pathSetsByLength = pathMap[len]; pathSetsByLength[pathSetsByLength.length] = pathSet; } } } // now that we have them all by length, convert each to a tree. var pathMapKeys = Object.keys(pathMap); var pathMapIdx = 0, pathMapLen = pathMapKeys.length; for (; pathMapIdx < pathMapLen; ++pathMapIdx) { var pathMapKey = pathMapKeys[pathMapIdx]; pathMap[pathMapKey] = toTree(pathMap[pathMapKey]); } } if (model._enablePathCollapse) { // Take the pathMapTree and create the collapsed paths and send those // off to the server. requestPaths = toPaths(request._pathMap); } else if (pathSetArrayBatch.length === 1) { // Single batch Array of path sets, just extract it requestPaths = pathSetArrayBatch[0]; } else { // Multiple batches of Arrays of path sets, shallowly flatten into an Array of path sets requestPaths = Array.prototype.concat.apply([], pathSetArrayBatch); } // Make the request. // You are probably wondering why this is not cancellable. If a request // goes out, and all the requests are removed, the request should not be // cancelled. The reasoning is that another request could come in, after // all callbacks have been removed and be deduped. Might as well keep this // around until it comes back. If at that point there are no requests then // we cancel at the callback above. var getRequest; try { getRequest = model._source.get(requestPaths, request._attemptCount); } catch (e) { callback(new InvalidSourceError()); return null; } // Ensures that the disposable is available for the outside to cancel. var jsonGraphData; var disposable = getRequest.subscribe( function(data) { jsonGraphData = data; }, function(err) { callback(err, jsonGraphData); }, function() { callback(null, jsonGraphData); } ); return disposable; }; },{"11":11,"136":136}],47:[function(require,module,exports){ var setJSONGraphs = require(66); var setPathValues = require(68); var InvalidSourceError = require(11); var emptyArray = []; var emptyDisposable = {dispose: function() {}}; /** * A set request is not an object like GetRequest. It simply only needs to * close over a couple values and its never batched together (at least not now). * * @private * @param {JSONGraphEnvelope} jsonGraph - * @param {Model} model - * @param {number} attemptCount * @param {Function} callback - */ var sendSetRequest = function(originalJsonGraph, model, attemptCount, callback) { var paths = originalJsonGraph.paths; var modelRoot = model._root; var errorSelector = modelRoot.errorSelector; var comparator = modelRoot.comparator; var boundPath = model._path; var resultingJsonGraphEnvelope; // This is analogous to GetRequest _merge / flushGetRequest // SetRequests are just considerably simplier. var setObservable; try { setObservable = model._source. set(originalJsonGraph, attemptCount); } catch (e) { callback(new InvalidSourceError()); return emptyDisposable; } var disposable = setObservable. subscribe(function onNext(jsonGraphEnvelope) { // When disposed, no data is inserted into. This can sync resolve // and if thats the case then its undefined. if (disposable && disposable.disposed) { return; } // onNext will insert all data into the model then save the json // envelope from the incoming result. model._path = emptyArray; var successfulPaths = setJSONGraphs(model, [{ paths: paths, jsonGraph: jsonGraphEnvelope.jsonGraph }], null, errorSelector, comparator); jsonGraphEnvelope.paths = successfulPaths[1]; model._path = boundPath; resultingJsonGraphEnvelope = jsonGraphEnvelope; }, function onError(dataSourceError) { if (disposable && disposable.disposed) { return; } model._path = emptyArray; setPathValues(model, paths.map(function(path) { return { path: path, value: dataSourceError }; }), null, errorSelector, comparator); model._path = boundPath; callback(dataSourceError); }, function onCompleted() { callback(null, resultingJsonGraphEnvelope); }); return disposable; }; module.exports = sendSetRequest; },{"11":11,"66":66,"68":68}],48:[function(require,module,exports){ /** * Will allow for state tracking of the current disposable. Also fulfills the * disposable interface. * @private */ var AssignableDisposable = function AssignableDisposable(disosableCallback) { this.disposed = false; this.currentDisposable = disosableCallback; }; AssignableDisposable.prototype = { /** * Disposes of the current disposable. This would be the getRequestCycle * disposable. */ dispose: function dispose() { if (this.disposed || !this.currentDisposable) { return; } this.disposed = true; // If the current disposable fulfills the disposable interface or just // a disposable function. var currentDisposable = this.currentDisposable; if (currentDisposable.dispose) { currentDisposable.dispose(); } else { currentDisposable(); } } }; module.exports = AssignableDisposable; },{}],49:[function(require,module,exports){ var ModelResponse = require(51); var InvalidSourceError = require(11); var pathSyntax = require(124); /** * @private * @augments ModelResponse */ function CallResponse(model, callPath, args, suffix, paths) { this.callPath = pathSyntax.fromPath(callPath); this.args = args; if (paths) { this.paths = paths.map(pathSyntax.fromPath); } if (suffix) { this.suffix = suffix.map(pathSyntax.fromPath); } this.model = model; } CallResponse.prototype = Object.create(ModelResponse.prototype); CallResponse.prototype._subscribe = function _subscribe(observer) { var callPath = this.callPath; var callArgs = this.args; var suffixes = this.suffix; var extraPaths = this.paths; var model = this.model; var rootModel = model._clone({ _path: [] }); var boundPath = model._path; var boundCallPath = boundPath.concat(callPath); /* eslint-disable consistent-return */ // Precisely the same error as the router when a call function does not // exist. if (!model._source) { observer.onError(new Error("function does not exist")); return; } var response, obs; try { obs = model._source. call(boundCallPath, callArgs, suffixes, extraPaths); } catch (e) { observer.onError(new InvalidSourceError(e)); return; } return obs. subscribe(function(res) { response = res; }, function(err) { observer.onError(err); }, function() { // Run the invalidations first then the follow up JSONGraph set. var invalidations = response.invalidated; if (invalidations && invalidations.length) { rootModel.invalidate.apply(rootModel, invalidations); } // The set rootModel. withoutDataSource(). set(response).subscribe(function(x) { observer.onNext(x); }, function(err) { observer.onError(err); }, function() { observer.onCompleted(); }); }); /* eslint-enable consistent-return */ }; module.exports = CallResponse; },{"11":11,"124":124,"51":51}],50:[function(require,module,exports){ var isArray = Array.isArray; var ModelResponse = require(51); var isPathValue = require(90); var isJSONEnvelope = require(87); var empty = {dispose: function() {}}; function InvalidateResponse(model, args) { // TODO: This should be removed. There should only be 1 type of arguments // coming in, but we have strayed from documentation. this._model = model; var groups = []; var group, groupType; var argIndex = -1; var argCount = args.length; // Validation of arguments have been moved out of this function. while (++argIndex < argCount) { var arg = args[argIndex]; var argType; if (isArray(arg)) { argType = "PathValues"; } else if (isPathValue(arg)) { argType = "PathValues"; } else if (isJSONEnvelope(arg)) { argType = "PathMaps"; } else { throw new Error("Invalid Input"); } if (groupType !== argType) { groupType = argType; group = { inputType: argType, arguments: [] }; groups.push(group); } group.arguments.push(arg); } this._groups = groups; } InvalidateResponse.prototype = Object.create(ModelResponse.prototype); InvalidateResponse.prototype.progressively = function progressively() { return this; }; InvalidateResponse.prototype._toJSONG = function _toJSONG() { return this; }; InvalidateResponse.prototype._subscribe = function _subscribe(observer) { var model = this._model; this._groups.forEach(function(group) { var inputType = group.inputType; var methodArgs = group.arguments; var operationName = "_invalidate" + inputType; var operationFunc = model[operationName]; operationFunc(model, methodArgs); }); observer.onCompleted(); return empty; }; module.exports = InvalidateResponse; },{"51":51,"87":87,"90":90}],51:[function(require,module,exports){ (function (Promise){(function (){ var ModelResponseObserver = require(52); var $$observable = require(158).default; var toEsObservable = require(107); /** * A ModelResponse is a container for the results of a get, set, or call operation performed on a Model. The ModelResponse provides methods which can be used to specify the output format of the data retrieved from a Model, as well as how that data is delivered. * @constructor ModelResponse * @augments Observable */ function ModelResponse(subscribe) { this._subscribe = subscribe; } ModelResponse.prototype[$$observable] = function SymbolObservable() { return toEsObservable(this); }; ModelResponse.prototype._toJSONG = function toJSONG() { return this; }; /** * The progressively method breaks the response up into two parts: the data immediately available in the Model cache, and the data in the Model cache after the missing data has been retrieved from the DataSource. * The progressively method creates a ModelResponse that immediately returns the requested data that is available in the Model cache. If any requested paths are not available in the cache, the ModelResponse will send another JSON message with all of the requested data after it has been retrieved from the DataSource. * @name progressively * @memberof ModelResponse.prototype * @function * @return {ModelResponse.} the values found at the requested paths. * @example var dataSource = (new falcor.Model({ cache: { user: { name: "Steve", surname: "McGuire", age: 31 } } })).asDataSource(); var model = new falcor.Model({ source: dataSource, cache: { user: { name: "Steve", surname: "McGuire" } } }); model. get(["user",["name", "surname", "age"]]). progressively(). // this callback will be invoked twice, once with the data in the // Model cache, and again with the additional data retrieved from the DataSource. subscribe(function(json){ console.log(JSON.stringify(json,null,4)); }); // prints... // { // "json": { // "user": { // "name": "Steve", // "surname": "McGuire" // } // } // } // ...and then prints... // { // "json": { // "user": { // "name": "Steve", // "surname": "McGuire", // "age": 31 // } // } // } */ ModelResponse.prototype.progressively = function progressively() { return this; }; ModelResponse.prototype.subscribe = ModelResponse.prototype.forEach = function subscribe(a, b, c) { var observer = new ModelResponseObserver(a, b, c); var subscription = this._subscribe(observer); switch (typeof subscription) { case "function": return { dispose: function() { if (observer._closed) { return; } observer._closed = true; subscription(); } }; case "object": return { dispose: function() { if (observer._closed) { return; } observer._closed = true; if (subscription !== null) { subscription.dispose(); } } }; default: return { dispose: function() { observer._closed = true; } }; } }; ModelResponse.prototype.then = function then(onNext, onError) { /* global Promise */ var self = this; if (!self._promise) { self._promise = new Promise(function(resolve, reject) { var rejected = false; var values = []; self.subscribe( function(value) { values[values.length] = value; }, function(errors) { rejected = true; reject(errors); }, function() { var value = values; if (values.length <= 1) { value = values[0]; } if (rejected === false) { resolve(value); } } ); }); } return self._promise.then(onNext, onError); }; module.exports = ModelResponse; }).call(this)}).call(this,typeof Promise === "function" ? Promise : require(150)) },{"107":107,"150":150,"158":158,"52":52}],52:[function(require,module,exports){ var noop = require(94); /** * A ModelResponseObserver conform to the Observable's Observer contract. It accepts either an Observer or three optional callbacks which correspond to the Observer methods onNext, onError, and onCompleted. * The ModelResponseObserver wraps an Observer to enforce a variety of different invariants including: * 1. onError callback is only called once. * 2. onCompleted callback is only called once. * @constructor ModelResponseObserver */ function ModelResponseObserver( onNextOrObserver, onErrorFn, onCompletedFn ) { // if callbacks are passed, construct an Observer from them. Create a NOOP function for any missing callbacks. if (!onNextOrObserver || typeof onNextOrObserver !== "object") { this._observer = { onNext: ( typeof onNextOrObserver === "function" ? onNextOrObserver : noop ), onError: ( typeof onErrorFn === "function" ? onErrorFn : noop ), onCompleted: ( typeof onCompletedFn === "function" ? onCompletedFn : noop ) }; } // if an Observer is passed else { this._observer = { onNext: typeof onNextOrObserver.onNext === "function" ? function(value) { onNextOrObserver.onNext(value); } : noop, onError: typeof onNextOrObserver.onError === "function" ? function(error) { onNextOrObserver.onError(error); } : noop, onCompleted: ( typeof onNextOrObserver.onCompleted === "function" ? function() { onNextOrObserver.onCompleted(); } : noop ) }; } } ModelResponseObserver.prototype = { onNext: function(v) { if (!this._closed) { this._observer.onNext(v); } }, onError: function(e) { if (!this._closed) { this._closed = true; this._observer.onError(e); } }, onCompleted: function() { if (!this._closed) { this._closed = true; this._observer.onCompleted(); } } }; module.exports = ModelResponseObserver; },{"94":94}],53:[function(require,module,exports){ var ModelResponse = require(51); var checkCacheAndReport = require(54); var getRequestCycle = require(55); var empty = {dispose: function() {}}; var collectLru = require(39); var getSize = require(77); /** * The get response. It takes in a model and paths and starts * the request cycle. It has been optimized for cache first requests * and closures. * @param {Model} model - * @param {Array} paths - * @augments ModelResponse * @private */ var GetResponse = function GetResponse(model, paths, isJSONGraph, isProgressive, forceCollect) { this.model = model; this.currentRemainingPaths = paths; this.isJSONGraph = isJSONGraph || false; this.isProgressive = isProgressive || false; this.forceCollect = forceCollect || false; }; GetResponse.prototype = Object.create(ModelResponse.prototype); /** * Makes the output of a get response JSONGraph instead of json. * @private */ GetResponse.prototype._toJSONG = function _toJSONGraph() { return new GetResponse(this.model, this.currentRemainingPaths, true, this.isProgressive, this.forceCollect); }; /** * Progressively responding to data in the cache instead of once the whole * operation is complete. * @public */ GetResponse.prototype.progressively = function progressively() { return new GetResponse(this.model, this.currentRemainingPaths, this.isJSONGraph, true, this.forceCollect); }; /** * purely for the purposes of closure creation other than the initial * prototype created closure. * * @private */ GetResponse.prototype._subscribe = function _subscribe(observer) { var seed = [{}]; var errors = []; var model = this.model; var isJSONG = observer.isJSONG = this.isJSONGraph; var isProgressive = this.isProgressive; var results = checkCacheAndReport(model, this.currentRemainingPaths, observer, isProgressive, isJSONG, seed, errors); // If there are no results, finish. if (!results) { if (this.forceCollect) { var modelRoot = model._root; var modelCache = modelRoot.cache; var currentVersion = modelCache.$_version; collectLru(modelRoot, modelRoot.expired, getSize(modelCache), model._maxSize, model._collectRatio, currentVersion); } return empty; } // Starts the async request cycle. return getRequestCycle(this, model, results, observer, errors, 1); }; module.exports = GetResponse; },{"39":39,"51":51,"54":54,"55":55,"77":77}],54:[function(require,module,exports){ var gets = require(23); var getWithPathsAsJSONGraph = gets.getWithPathsAsJSONGraph; var getWithPathsAsPathMap = gets.getWithPathsAsPathMap; /** * Checks cache for the paths and reports if in progressive mode. If * there are missing paths then return the cache hit results. * * Return value (`results`) stores missing path information as 3 index-linked arrays: * `requestedMissingPaths` holds requested paths that were not found in cache * `optimizedMissingPaths` holds optimized versions of requested paths * * Note that requestedMissingPaths is not necessarily the list of paths requested by * user in model.get. It does not contain those paths that were found in * cache. It also breaks some path sets out into separate paths, those which * resolve to different optimized lengths after walking through any references in * cache. * This helps maintain a 1:1 correspondence between requested and optimized missing, * as well as their depth differences (or, length offsets). * * Example: Given cache: `{ lolomo: { 0: $ref('vid'), 1: $ref('a.b.c.d') }}`, * `model.get('lolomo[0..2].name').subscribe()` will result in the following * corresponding values: * index requestedMissingPaths optimizedMissingPaths * 0 ['lolomo', 0, 'name'] ['vid', 'name'] * 1 ['lolomo', 1, 'name'] ['a', 'b', 'c', 'd', 'name'] * 2 ['lolomo', 2, 'name'] ['lolomo', 2, 'name'] * * @param {Model} model - The model that the request was made with. * @param {Array} requestedMissingPaths - * @param {Boolean} progressive - * @param {Boolean} isJSONG - * @param {Function} onNext - * @param {Function} onError - * @param {Function} onCompleted - * @param {Object} seed - The state of the output * @returns {Object} results - * * @private */ module.exports = function checkCacheAndReport(model, requestedPaths, observer, progressive, isJSONG, seed, errors) { // checks the cache for the data. var results = isJSONG ? getWithPathsAsJSONGraph(model, requestedPaths, seed) : getWithPathsAsPathMap(model, requestedPaths, seed); // We are done when there are no missing paths or the model does not // have a dataSource to continue on fetching from. var valueNode = results.values && results.values[0]; var completed = !results.requestedMissingPaths || !results.requestedMissingPaths.length || !model._source; // Copy the errors into the total errors array. if (results.errors) { var errs = results.errors; var errorsLength = errors.length; for (var i = 0, len = errs.length; i < len; ++i, ++errorsLength) { errors[errorsLength] = errs[i]; } } // Report locally available values if: // - the request is in progressive mode, or // - the request is complete and values were found if (progressive || (completed && valueNode !== undefined)) { observer.onNext(valueNode); } // We must communicate critical errors from get that are critical // errors such as bound path is broken or this is a JSONGraph request // with a bound path. if (results.criticalError) { observer.onError(results.criticalError); return null; } // if there are missing paths, then lets return them. if (completed) { if (errors.length) { observer.onError(errors); } else { observer.onCompleted(); } return null; } // Return the results object. return results; }; },{"23":23}],55:[function(require,module,exports){ var checkCacheAndReport = require(54); var MaxRetryExceededError = require(12); var collectLru = require(39); var getSize = require(77); var AssignableDisposable = require(48); var InvalidSourceError = require(11); /** * The get request cycle for checking the cache and reporting * values. If there are missing paths then the async request cycle to * the data source is performed until all paths are resolved or max * requests are made. * @param {GetResponse} getResponse - * @param {Model} model - The model that the request was made with. * @param {Object} results - * @param {Function} onNext - * @param {Function} onError - * @param {Function} onCompleted - * @private */ module.exports = function getRequestCycle(getResponse, model, results, observer, errors, count) { // we have exceeded the maximum retry limit. if (count > model._maxRetries) { observer.onError(new MaxRetryExceededError(results.optimizedMissingPaths)); return { dispose: function() {} }; } var requestQueue = model._request; var requestedMissingPaths = results.requestedMissingPaths; var optimizedMissingPaths = results.optimizedMissingPaths; var disposable = new AssignableDisposable(); // We need to prepend the bound path to all requested missing paths and // pass those into the requestQueue. var boundRequestedMissingPaths = []; var boundPath = model._path; if (boundPath.length) { for (var i = 0, len = requestedMissingPaths.length; i < len; ++i) { boundRequestedMissingPaths[i] = boundPath.concat(requestedMissingPaths[i]); } } // No bound path, no array copy and concat. else { boundRequestedMissingPaths = requestedMissingPaths; } var currentRequestDisposable = requestQueue. get(boundRequestedMissingPaths, optimizedMissingPaths, count, function(err, data, hasInvalidatedResult) { if (model._treatDataSourceErrorsAsJSONGraphErrors ? err instanceof InvalidSourceError : !!err) { if (results.hasValues) { observer.onNext(results.values && results.values[0]); } observer.onError(err); return; } var nextRequestedMissingPaths; var nextSeed; // If merging over an existing branch structure with refs has invalidated our intermediate json, // we want to start over and re-get all requested paths with a fresh seed if (hasInvalidatedResult) { nextRequestedMissingPaths = getResponse.currentRemainingPaths; nextSeed = [{}]; } else { nextRequestedMissingPaths = requestedMissingPaths; nextSeed = results.values; } // Once the request queue finishes, check the cache and bail if // we can. var nextResults = checkCacheAndReport(model, nextRequestedMissingPaths, observer, getResponse.isProgressive, getResponse.isJSONGraph, nextSeed, errors); // If there are missing paths coming back form checkCacheAndReport // the its reported from the core cache check method. if (nextResults) { // update the which disposable to use. disposable.currentDisposable = getRequestCycle(getResponse, model, nextResults, observer, errors, count + 1); } // We have finished. Since we went to the dataSource, we must // collect on the cache. else { var modelRoot = model._root; var modelCache = modelRoot.cache; var currentVersion = modelCache.$_version; collectLru(modelRoot, modelRoot.expired, getSize(modelCache), model._maxSize, model._collectRatio, currentVersion); } }); disposable.currentDisposable = currentRequestDisposable; return disposable; }; },{"11":11,"12":12,"39":39,"48":48,"54":54,"77":77}],56:[function(require,module,exports){ var GetResponse = require(53); /** * Performs a get on the cache and if there are missing paths * then the request will be forwarded to the get request cycle. * @private */ module.exports = function getWithPaths(paths) { return new GetResponse(this, paths); }; },{"53":53}],57:[function(require,module,exports){ var pathSyntax = require(124); var ModelResponse = require(51); var GET_VALID_INPUT = require(58); var validateInput = require(105); var GetResponse = require(53); /** * Performs a get on the cache and if there are missing paths * then the request will be forwarded to the get request cycle. * @private */ module.exports = function get() { // Validates the input. If the input is not pathSets or strings then we // will onError. var out = validateInput(arguments, GET_VALID_INPUT, "get"); if (out !== true) { return new ModelResponse(function(o) { o.onError(out); }); } var paths = pathSyntax.fromPathsOrPathValues(arguments); return new GetResponse(this, paths); }; },{"105":105,"124":124,"51":51,"53":53,"58":58}],58:[function(require,module,exports){ module.exports = { path: true, pathSyntax: true }; },{}],59:[function(require,module,exports){ var ModelResponse = require(51); var pathSyntax = require(124); var isArray = Array.isArray; var isPathValue = require(90); var isJSONGraphEnvelope = require(88); var isJSONEnvelope = require(87); var setRequestCycle = require(62); /** * The set response is responsible for doing the request loop for the set * operation and subscribing to the follow up get. * * The constructors job is to parse out the arguments and put them in their * groups. The following subscribe will do the actual cache set and dataSource * operation remoting. * * @param {Model} model - * @param {Array} args - The array of arguments that can be JSONGraph, JSON, or * pathValues. * @param {Boolean} isJSONGraph - if the request is a jsonGraph output format. * @param {Boolean} isProgressive - progressive output. * @augments ModelResponse * @private */ var SetResponse = function SetResponse(model, args, isJSONGraph, isProgressive) { // The response properties. this._model = model; this._isJSONGraph = isJSONGraph || false; this._isProgressive = isProgressive || false; this._initialArgs = args; this._value = [{}]; var groups = []; var group, groupType; var argIndex = -1; var argCount = args.length; // Validation of arguments have been moved out of this function. while (++argIndex < argCount) { var arg = args[argIndex]; var argType; if (isArray(arg) || typeof arg === "string") { arg = pathSyntax.fromPath(arg); argType = "PathValues"; } else if (isPathValue(arg)) { arg.path = pathSyntax.fromPath(arg.path); argType = "PathValues"; } else if (isJSONGraphEnvelope(arg)) { argType = "JSONGs"; } else if (isJSONEnvelope(arg)) { argType = "PathMaps"; } if (groupType !== argType) { groupType = argType; group = { inputType: argType, arguments: [] }; groups.push(group); } group.arguments.push(arg); } this._groups = groups; }; SetResponse.prototype = Object.create(ModelResponse.prototype); /** * The subscribe function will setup the remoting of the operation and cache * setting. * * @private */ SetResponse.prototype._subscribe = function _subscribe(observer) { var groups = this._groups; var model = this._model; var isJSONGraph = this._isJSONGraph; var isProgressive = this._isProgressive; // Starts the async request cycle. return setRequestCycle( model, observer, groups, isJSONGraph, isProgressive, 1); }; /** * Makes the output of a get response JSONGraph instead of json. * @private */ SetResponse.prototype._toJSONG = function _toJSONGraph() { return new SetResponse(this._model, this._initialArgs, true, this._isProgressive); }; /** * Progressively responding to data in the cache instead of once the whole * operation is complete. * @public */ SetResponse.prototype.progressively = function progressively() { return new SetResponse(this._model, this._initialArgs, this._isJSONGraph, true); }; module.exports = SetResponse; },{"124":124,"51":51,"62":62,"87":87,"88":88,"90":90}],60:[function(require,module,exports){ var setValidInput = require(63); var validateInput = require(105); var SetResponse = require(59); var ModelResponse = require(51); module.exports = function set() { var out = validateInput(arguments, setValidInput, "set"); if (out !== true) { return new ModelResponse(function(o) { o.onError(out); }); } var argsIdx = -1; var argsLen = arguments.length; var args = []; while (++argsIdx < argsLen) { args[argsIdx] = arguments[argsIdx]; } return new SetResponse(this, args); }; },{"105":105,"51":51,"59":59,"63":63}],61:[function(require,module,exports){ var arrayFlatMap = require(71); /** * Takes the groups that are created in the SetResponse constructor and sets * them into the cache. */ module.exports = function setGroupsIntoCache(model, groups) { var modelRoot = model._root; var errorSelector = modelRoot.errorSelector; var groupIndex = -1; var groupCount = groups.length; var requestedPaths = []; var optimizedPaths = []; var returnValue = { requestedPaths: requestedPaths, optimizedPaths: optimizedPaths }; // Takes each of the groups and normalizes their input into // requested paths and optimized paths. while (++groupIndex < groupCount) { var group = groups[groupIndex]; var inputType = group.inputType; var methodArgs = group.arguments; if (methodArgs.length > 0) { var operationName = "_set" + inputType; var operationFunc = model[operationName]; var successfulPaths = operationFunc(model, methodArgs, null, errorSelector); optimizedPaths.push.apply(optimizedPaths, successfulPaths[1]); if (inputType === "PathValues") { requestedPaths.push.apply(requestedPaths, methodArgs.map(pluckPath)); } else if (inputType === "JSONGs") { requestedPaths.push.apply(requestedPaths, arrayFlatMap(methodArgs, pluckEnvelopePaths)); } else { requestedPaths.push.apply(requestedPaths, successfulPaths[0]); } } } return returnValue; }; function pluckPath(pathValue) { return pathValue.path; } function pluckEnvelopePaths(jsonGraphEnvelope) { return jsonGraphEnvelope.paths; } },{"71":71}],62:[function(require,module,exports){ var emptyArray = []; var AssignableDisposable = require(48); var GetResponse = require(53); var setGroupsIntoCache = require(61); var getWithPathsAsPathMap = require(23).getWithPathsAsPathMap; var InvalidSourceError = require(11); var MaxRetryExceededError = require(12); /** * The request cycle for set. This is responsible for requesting to dataSource * and allowing disposing inflight requests. */ module.exports = function setRequestCycle(model, observer, groups, isJSONGraph, isProgressive, count) { var requestedAndOptimizedPaths = setGroupsIntoCache(model, groups); var optimizedPaths = requestedAndOptimizedPaths.optimizedPaths; var requestedPaths = requestedAndOptimizedPaths.requestedPaths; // we have exceeded the maximum retry limit. if (count > model._maxRetries) { observer.onError(new MaxRetryExceededError(optimizedPaths)); return { dispose: function() {} }; } var isMaster = model._source === undefined; // Local set only. We perform a follow up get. If performance is ever // a requirement simply requiring in checkCacheAndReport and use get request // internals. Figured this is more "pure". if (isMaster) { return subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph, isProgressive); } // Progressively output the data from the first set. var prevVersion; if (isProgressive) { var results = getWithPathsAsPathMap(model, requestedPaths, [{}]); if (results.criticalError) { observer.onError(results.criticalError); return null; } observer.onNext(results.values[0]); prevVersion = model._root.cache.$_version; } var currentJSONGraph = getJSONGraph(model, optimizedPaths); var disposable = new AssignableDisposable(); // Sends out the setRequest. The Queue will call the callback with the // JSONGraph envelope / error. var requestDisposable = model._request. // TODO: There is error handling that has not been addressed yet. // If disposed before this point then the sendSetRequest will not // further any callbacks. Therefore, if we are at this spot, we are // not disposed yet. set(currentJSONGraph, count, function(error, jsonGraphEnv) { if (error instanceof InvalidSourceError) { observer.onError(error); return; } // TODO: This seems like there are errors with this approach, but // for sanity sake I am going to keep this logic in here until a // rethink can be done. var isCompleted = false; if (error || optimizedPaths.length === jsonGraphEnv.paths.length) { isCompleted = true; } // If we're in progressive mode and nothing changed in the meantime, we're done if (isProgressive) { var nextVersion = model._root.cache.$_version; var versionChanged = nextVersion !== prevVersion; if (!versionChanged) { observer.onCompleted(); return; } } // Happy case. One request to the dataSource will fulfill the // required paths. if (isCompleted) { disposable.currentDisposable = subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph, isProgressive); } // TODO: The unhappy case. I am unsure how this can even be // achieved. else { // We need to restart the setRequestCycle. setRequestCycle(model, observer, groups, isJSONGraph, isProgressive, count + 1); } }); // Sets the current disposable as the requestDisposable. disposable.currentDisposable = requestDisposable; return disposable; }; function getJSONGraph(model, optimizedPaths) { var boundPath = model._path; var envelope = {}; model._path = emptyArray; model._getPathValuesAsJSONG(model._materialize().withoutDataSource(), optimizedPaths, [envelope]); model._path = boundPath; return envelope; } function subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph, isProgressive) { // Creates a new response and subscribes to it with the original observer. // Also sets forceCollect to true, incase the operation is synchronous and // exceeds the cache limit size var response = new GetResponse(model, requestedPaths, isJSONGraph, isProgressive, true); return response.subscribe(observer); } },{"11":11,"12":12,"23":23,"48":48,"53":53,"61":61}],63:[function(require,module,exports){ module.exports = { pathValue: true, pathSyntax: true, json: true, jsonGraph: true }; },{}],64:[function(require,module,exports){ var empty = {dispose: function() {}}; function ImmediateScheduler() {} ImmediateScheduler.prototype.schedule = function schedule(action) { action(); return empty; }; ImmediateScheduler.prototype.scheduleWithState = function scheduleWithState(state, action) { action(this, state); return empty; }; module.exports = ImmediateScheduler; },{}],65:[function(require,module,exports){ function TimeoutScheduler(delay) { this.delay = delay; } var TimerDisposable = function TimerDisposable(id) { this.id = id; this.disposed = false; }; TimeoutScheduler.prototype.schedule = function schedule(action) { var id = setTimeout(action, this.delay); return new TimerDisposable(id); }; TimeoutScheduler.prototype.scheduleWithState = function scheduleWithState(state, action) { var self = this; var id = setTimeout(function() { action(self, state); }, this.delay); return new TimerDisposable(id); }; TimerDisposable.prototype.dispose = function() { if (this.disposed) { return; } clearTimeout(this.id); this.disposed = true; }; module.exports = TimeoutScheduler; },{}],66:[function(require,module,exports){ var createHardlink = require(73); var $ref = require(110); var isExpired = require(83); var isFunction = require(85); var isPrimitive = require(91); var expireNode = require(75); var iterateKeySet = require(136).iterateKeySet; var incrementVersion = require(81); var mergeJSONGraphNode = require(92); var NullInPathError = require(13); /** * Merges a list of {@link JSONGraphEnvelope}s into a {@link JSONGraph}. * @function * @param {Object} model - the Model for which to merge the {@link JSONGraphEnvelope}s. * @param {Array.} jsonGraphEnvelopes - the {@link JSONGraphEnvelope}s to merge. * @return {Array.>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values. */ module.exports = function setJSONGraphs(model, jsonGraphEnvelopes, x, errorSelector, comparator, replacedPaths) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var cache = modelRoot.cache; var initialVersion = cache.$_version; var requestedPath = []; var optimizedPath = []; var requestedPaths = []; var optimizedPaths = []; var jsonGraphEnvelopeIndex = -1; var jsonGraphEnvelopeCount = jsonGraphEnvelopes.length; while (++jsonGraphEnvelopeIndex < jsonGraphEnvelopeCount) { var jsonGraphEnvelope = jsonGraphEnvelopes[jsonGraphEnvelopeIndex]; var paths = jsonGraphEnvelope.paths; var jsonGraph = jsonGraphEnvelope.jsonGraph; var pathIndex = -1; var pathCount = paths.length; while (++pathIndex < pathCount) { var path = paths[pathIndex]; optimizedPath.index = 0; setJSONGraphPathSet( path, 0, cache, cache, cache, jsonGraph, jsonGraph, jsonGraph, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); } } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } return [requestedPaths, optimizedPaths]; }; /* eslint-disable no-constant-condition */ function setJSONGraphPathSet( path, depth, root, parent, node, messageRoot, messageParent, message, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var note = {}; var branch = depth < path.length - 1; var keySet = path[depth]; var key = iterateKeySet(keySet, note); var optimizedIndex = optimizedPath.index; do { requestedPath.depth = depth; var results = setNode( root, parent, node, messageRoot, messageParent, message, key, branch, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); requestedPath[depth] = key; requestedPath.index = depth; optimizedPath[optimizedPath.index++] = key; var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { setJSONGraphPathSet( path, depth + 1, root, nextParent, nextNode, messageRoot, results[3], results[2], requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); } else { requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1)); optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index)); } } key = iterateKeySet(keySet, note); if (note.done) { break; } optimizedPath.index = optimizedIndex; } while (true); } /* eslint-enable */ var _result = new Array(4); function setReference( root, node, messageRoot, message, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var reference = node.value; optimizedPath.length = 0; optimizedPath.push.apply(optimizedPath, reference); if (isExpired(node)) { optimizedPath.index = reference.length; expireNode(node, expired, lru); _result[0] = undefined; _result[1] = root; _result[2] = message; _result[3] = messageRoot; return _result; } var index = 0; var container = node; var count = reference.length - 1; var parent = node = root; var messageParent = message = messageRoot; do { var key = reference[index]; var branch = index < count; optimizedPath.index = index; var results = setNode( root, parent, node, messageRoot, messageParent, message, key, branch, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { optimizedPath.index = index; return results; } parent = results[1]; message = results[2]; messageParent = results[3]; } while (index++ < count); optimizedPath.index = index; if (container.$_context !== node) { createHardlink(container, node); } _result[0] = node; _result[1] = parent; _result[2] = message; _result[3] = messageParent; return _result; } function setNode( root, parent, node, messageRoot, messageParent, message, key, branch, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var type = node.$type; while (type === $ref) { var results = setReference( root, node, messageRoot, message, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; message = results[2]; messageParent = results[3]; type = node.$type; } if (type !== void 0) { _result[0] = node; _result[1] = parent; _result[2] = message; _result[3] = messageParent; return _result; } if (key == null) { if (branch) { throw new NullInPathError({ requestedPath: requestedPath }); } else if (node) { key = node.$_key; } } else { parent = node; messageParent = message; node = parent[key]; message = messageParent && messageParent[key]; } node = mergeJSONGraphNode( parent, node, message, key, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); _result[0] = node; _result[1] = parent; _result[2] = message; _result[3] = messageParent; return _result; } },{"110":110,"13":13,"136":136,"73":73,"75":75,"81":81,"83":83,"85":85,"91":91,"92":92}],67:[function(require,module,exports){ var createHardlink = require(73); var __prefix = require(36); var $ref = require(110); var getBoundValue = require(17); var isArray = Array.isArray; var hasOwn = require(80); var isObject = require(89); var isExpired = require(84); var isFunction = require(85); var isPrimitive = require(91); var expireNode = require(75); var incrementVersion = require(81); var mergeValueOrInsertBranch = require(93); var NullInPathError = require(13); /** * Sets a list of {@link PathMapEnvelope}s into a {@link JSONGraph}. * @function * @param {Object} model - the Model for which to insert the PathMaps. * @param {Array.} pathMapEnvelopes - the a list of {@link PathMapEnvelope}s to set. * @return {Array.>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values. */ module.exports = function setPathMaps(model, pathMapEnvelopes, x, errorSelector, comparator) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; var parent = node.$_parent || cache; var initialVersion = cache.$_version; var requestedPath = []; var requestedPaths = []; var optimizedPaths = []; var optimizedIndex = bound.length; var pathMapIndex = -1; var pathMapCount = pathMapEnvelopes.length; while (++pathMapIndex < pathMapCount) { var pathMapEnvelope = pathMapEnvelopes[pathMapIndex]; var optimizedPath = bound.slice(0); optimizedPath.index = optimizedIndex; setPathMap( pathMapEnvelope.json, 0, cache, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } return [requestedPaths, optimizedPaths]; }; /* eslint-disable no-constant-condition */ function setPathMap( pathMap, depth, root, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector) { var keys = getKeys(pathMap); if (keys && keys.length) { var keyIndex = 0; var keyCount = keys.length; var optimizedIndex = optimizedPath.index; do { var key = keys[keyIndex]; var child = pathMap[key]; var branch = isObject(child) && !child.$type; requestedPath.depth = depth; var results = setNode( root, parent, node, key, child, branch, false, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); requestedPath[depth] = key; requestedPath.index = depth; optimizedPath[optimizedPath.index++] = key; var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { setPathMap( child, depth + 1, root, nextParent, nextNode, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } else { requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1)); optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index)); } } if (++keyIndex >= keyCount) { break; } optimizedPath.index = optimizedIndex; } while (true); } } /* eslint-enable */ function setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector) { var reference = node.value; optimizedPath.length = 0; optimizedPath.push.apply(optimizedPath, reference); if (isExpired(node)) { optimizedPath.index = reference.length; expireNode(node, expired, lru); return [undefined, root]; } var container = node; var parent = root; node = node.$_context; if (node != null) { parent = node.$_parent || root; optimizedPath.index = reference.length; } else { var index = 0; var count = reference.length - 1; optimizedPath.index = index; parent = node = root; do { var key = reference[index]; var branch = index < count; var results = setNode( root, parent, node, key, value, branch, true, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); node = results[0]; if (isPrimitive(node)) { optimizedPath.index = index; return results; } parent = results[1]; } while (index++ < count); optimizedPath.index = index; if (container.$_context !== node) { createHardlink(container, node); } } return [node, parent]; } function setNode( root, parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector) { var type = node.$type; while (type === $ref) { var results = setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node && node.$type; } if (type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new NullInPathError({ requestedPath: requestedPath }); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } node = mergeValueOrInsertBranch( parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); return [node, parent]; } function getKeys(pathMap) { if (isObject(pathMap) && !pathMap.$type) { var keys = []; var itr = 0; if (isArray(pathMap)) { keys[itr++] = "length"; } for (var key in pathMap) { if (key[0] === __prefix || !hasOwn(pathMap, key)) { continue; } keys[itr++] = key; } return keys; } return void 0; } },{"110":110,"13":13,"17":17,"36":36,"73":73,"75":75,"80":80,"81":81,"84":84,"85":85,"89":89,"91":91,"93":93}],68:[function(require,module,exports){ var createHardlink = require(73); var $ref = require(110); var getBoundValue = require(17); var isExpired = require(84); var isFunction = require(85); var isPrimitive = require(91); var expireNode = require(75); var iterateKeySet = require(136).iterateKeySet; var incrementVersion = require(81); var mergeValueOrInsertBranch = require(93); var NullInPathError = require(13); /** * Sets a list of {@link PathValue}s into a {@link JSONGraph}. * @function * @param {Object} model - the Model for which to insert the {@link PathValue}s. * @param {Array.} pathValues - the list of {@link PathValue}s to set. * @return {Array.>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values. */ module.exports = function setPathValues(model, pathValues, x, errorSelector, comparator) { var modelRoot = model._root; var lru = modelRoot; var expired = modelRoot.expired; var version = incrementVersion(); var bound = model._path; var cache = modelRoot.cache; var node = bound.length ? getBoundValue(model, bound).value : cache; var parent = node.$_parent || cache; var initialVersion = cache.$_version; var requestedPath = []; var requestedPaths = []; var optimizedPaths = []; var optimizedIndex = bound.length; var pathValueIndex = -1; var pathValueCount = pathValues.length; while (++pathValueIndex < pathValueCount) { var pathValue = pathValues[pathValueIndex]; var path = pathValue.path; var value = pathValue.value; var optimizedPath = bound.slice(0); optimizedPath.index = optimizedIndex; setPathSet( value, path, 0, cache, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } var newVersion = cache.$_version; var rootChangeHandler = modelRoot.onChange; if (isFunction(rootChangeHandler) && initialVersion !== newVersion) { rootChangeHandler(); } return [requestedPaths, optimizedPaths]; }; /* eslint-disable no-constant-condition */ function setPathSet( value, path, depth, root, parent, node, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var note = {}; var branch = depth < path.length - 1; var keySet = path[depth]; var key = iterateKeySet(keySet, note); var optimizedIndex = optimizedPath.index; do { requestedPath.depth = depth; var results = setNode( root, parent, node, key, value, branch, false, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); requestedPath[depth] = key; requestedPath.index = depth; optimizedPath[optimizedPath.index++] = key; var nextNode = results[0]; var nextParent = results[1]; if (nextNode) { if (branch) { setPathSet( value, path, depth + 1, root, nextParent, nextNode, requestedPaths, optimizedPaths, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector ); } else { requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1)); optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index)); } } key = iterateKeySet(keySet, note); if (note.done) { break; } optimizedPath.index = optimizedIndex; } while (true); } /* eslint-enable */ function setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var reference = node.value; optimizedPath.length = 0; optimizedPath.push.apply(optimizedPath, reference); if (isExpired(node)) { optimizedPath.index = reference.length; expireNode(node, expired, lru); return [undefined, root]; } var container = node; var parent = root; node = node.$_context; if (node != null) { parent = node.$_parent || root; optimizedPath.index = reference.length; } else { var index = 0; var count = reference.length - 1; parent = node = root; do { var key = reference[index]; var branch = index < count; optimizedPath.index = index; var results = setNode( root, parent, node, key, value, branch, true, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { optimizedPath.index = index; return results; } parent = results[1]; } while (index++ < count); optimizedPath.index = index; if (container.$_context !== node) { createHardlink(container, node); } } return [node, parent]; } function setNode( root, parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var type = node.$type; while (type === $ref) { var results = setReference( value, root, node, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); node = results[0]; if (isPrimitive(node)) { return results; } parent = results[1]; type = node.$type; } if (branch && type !== void 0) { return [node, parent]; } if (key == null) { if (branch) { throw new NullInPathError({ requestedPath: requestedPath }); } else if (node) { key = node.$_key; } } else { parent = node; node = parent[key]; } node = mergeValueOrInsertBranch( parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths ); return [node, parent]; } },{"110":110,"13":13,"136":136,"17":17,"73":73,"75":75,"81":81,"84":84,"85":85,"91":91,"93":93}],69:[function(require,module,exports){ var jsong = require(120); var ModelResponse = require(51); var isPathValue = require(90); module.exports = function setValue(pathArg, valueArg) { var value = isPathValue(pathArg) ? pathArg : jsong.pathValue(pathArg, valueArg); var pathIdx = 0; var path = value.path; var pathLen = path.length; while (++pathIdx < pathLen) { if (typeof path[pathIdx] === "object") { /* eslint-disable no-loop-func */ return new ModelResponse(function(o) { o.onError(new Error("Paths must be simple paths")); }); /* eslint-enable no-loop-func */ } } var self = this; return new ModelResponse(function(obs) { return self.set(value).subscribe(function(data) { var curr = data.json; var depth = -1; var length = path.length; while (curr && ++depth < length) { curr = curr[path[depth]]; } obs.onNext(curr); }, function(err) { obs.onError(err); }, function() { obs.onCompleted(); }); }); }; },{"120":120,"51":51,"90":90}],70:[function(require,module,exports){ var pathSyntax = require(124); var isPathValue = require(90); var setPathValues = require(68); module.exports = function setValueSync(pathArg, valueArg, errorSelectorArg, comparatorArg) { var path = pathSyntax.fromPath(pathArg); var value = valueArg; var errorSelector = errorSelectorArg; // XXX comparator is never used. var comparator = comparatorArg; if (isPathValue(path)) { comparator = errorSelector; errorSelector = value; value = path; } else { value = { path: path, value: value }; } if (isPathValue(value) === false) { throw new Error("Model#setValueSync must be called with an Array path."); } if (typeof errorSelector !== "function") { errorSelector = this._root._errorSelector; } if (typeof comparator !== "function") { comparator = this._root._comparator; } this._syncCheck("setValueSync"); setPathValues(this, [value]); return this._getValueSync(value.path); }; },{"124":124,"68":68,"90":90}],71:[function(require,module,exports){ module.exports = function arrayFlatMap(array, selector) { var index = -1; var i = -1; var n = array.length; var array2 = []; while (++i < n) { var array3 = selector(array[i], i, array); var j = -1; var k = array3.length; while (++j < k) { array2[++index] = array3[j]; } } return array2; }; },{}],72:[function(require,module,exports){ var privatePrefix = require(34); var hasOwn = require(80); var isArray = Array.isArray; var isObject = require(89); module.exports = function clone(value) { var dest = value; if (isObject(dest)) { dest = isArray(value) ? [] : {}; var src = value; for (var key in src) { if (key.lastIndexOf(privatePrefix, 0) === 0 || !hasOwn(src, key)) { continue; } dest[key] = src[key]; } } return dest; }; },{"34":34,"80":80,"89":89}],73:[function(require,module,exports){ var __ref = require(35); module.exports = function createHardlink(from, to) { // create a back reference // eslint-disable-next-line camelcase var backRefs = to.$_refsLength || 0; to[__ref + backRefs] = from; // eslint-disable-next-line camelcase to.$_refsLength = backRefs + 1; // create a hard reference // eslint-disable-next-line camelcase from.$_refIndex = backRefs; // eslint-disable-next-line camelcase from.$_context = to; }; },{"35":35}],74:[function(require,module,exports){ var version = null; exports.setVersion = function setCacheVersion(newVersion) { version = newVersion; }; exports.getVersion = function getCacheVersion() { return version; }; },{}],75:[function(require,module,exports){ var splice = require(41); module.exports = function expireNode(node, expired, lru) { // eslint-disable-next-line camelcase if (!node.$_invalidated) { // eslint-disable-next-line camelcase node.$_invalidated = true; expired.push(node); splice(lru, node); } return node; }; },{"41":41}],76:[function(require,module,exports){ var isObject = require(89); module.exports = function getSize(node) { return isObject(node) && node.$expires || undefined; }; },{"89":89}],77:[function(require,module,exports){ var isObject = require(89); module.exports = function getSize(node) { return isObject(node) && node.$size || 0; }; },{"89":89}],78:[function(require,module,exports){ var isObject = require(89); module.exports = function getTimestamp(node) { return isObject(node) && node.$timestamp || undefined; }; },{"89":89}],79:[function(require,module,exports){ var isObject = require(89); module.exports = function getType(node, anyType) { var type = isObject(node) && node.$type || void 0; if (anyType && type) { return "branch"; } return type; }; },{"89":89}],80:[function(require,module,exports){ var isObject = require(89); var hasOwn = Object.prototype.hasOwnProperty; module.exports = function(obj, prop) { return isObject(obj) && hasOwn.call(obj, prop); }; },{"89":89}],81:[function(require,module,exports){ var version = 1; module.exports = function incrementVersion() { return version++; }; module.exports.getCurrentVersion = function getCurrentVersion() { return version; }; },{}],82:[function(require,module,exports){ /* eslint-disable camelcase */ module.exports = function insertNode(node, parent, key, version, optimizedPath) { node.$_key = key; node.$_parent = parent; if (version !== undefined) { node.$_version = version; } if (!node.$_absolutePath) { if (Array.isArray(key)) { node.$_absolutePath = optimizedPath.slice(0, optimizedPath.index); Array.prototype.push.apply(node.$_absolutePath, key); } else { node.$_absolutePath = optimizedPath.slice(0, optimizedPath.index); node.$_absolutePath.push(key); } } parent[key] = node; return node; }; },{}],83:[function(require,module,exports){ var now = require(95); var $now = require(112); var $never = require(111); module.exports = function isAlreadyExpired(node) { var exp = node.$expires; return (exp != null) && ( exp !== $never) && ( exp !== $now) && ( exp < now()); }; },{"111":111,"112":112,"95":95}],84:[function(require,module,exports){ var now = require(95); var $now = require(112); var $never = require(111); module.exports = function isExpired(node) { var exp = node.$expires; return (exp != null) && ( exp !== $never ) && ( exp === $now || exp < now()); }; },{"111":111,"112":112,"95":95}],85:[function(require,module,exports){ var functionTypeof = "function"; module.exports = function isFunction(func) { return Boolean(func) && typeof func === functionTypeof; }; },{}],86:[function(require,module,exports){ var privatePrefix = require(34); /** * Determined if the key passed in is an internal key. * * @param {String} x The key * @private * @returns {Boolean} */ module.exports = function isInternalKey(x) { return x === "$size" || x.lastIndexOf(privatePrefix, 0) === 0; }; },{"34":34}],87:[function(require,module,exports){ var isObject = require(89); module.exports = function isJSONEnvelope(envelope) { return isObject(envelope) && ("json" in envelope); }; },{"89":89}],88:[function(require,module,exports){ var isArray = Array.isArray; var isObject = require(89); module.exports = function isJSONGraphEnvelope(envelope) { return isObject(envelope) && isArray(envelope.paths) && ( isObject(envelope.jsonGraph) || isObject(envelope.jsong) || isObject(envelope.json) || isObject(envelope.values) || isObject(envelope.value) ); }; },{"89":89}],89:[function(require,module,exports){ var objTypeof = "object"; module.exports = function isObject(value) { return value !== null && typeof value === objTypeof; }; },{}],90:[function(require,module,exports){ var isArray = Array.isArray; var isObject = require(89); module.exports = function isPathValue(pathValue) { return isObject(pathValue) && ( isArray(pathValue.path) || ( typeof pathValue.path === "string" )); }; },{"89":89}],91:[function(require,module,exports){ var objTypeof = "object"; module.exports = function isPrimitive(value) { return value == null || typeof value !== objTypeof; }; },{}],92:[function(require,module,exports){ var $ref = require(110); var $error = require(109); var getSize = require(77); var getTimestamp = require(78); var isObject = require(89); var isExpired = require(84); var isFunction = require(85); var wrapNode = require(106); var insertNode = require(82); var expireNode = require(75); var replaceNode = require(99); var updateNodeAncestors = require(104); var reconstructPath = require(96); module.exports = function mergeJSONGraphNode( parent, node, message, key, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var sizeOffset; var cType, mType, cIsObject, mIsObject, cTimestamp, mTimestamp; var nodeValue = node && node.value !== undefined ? node.value : node; // If the cache and message are the same, we can probably return early: // - If they're both nullsy, // - If null then the node needs to be wrapped in an atom and inserted. // This happens from whole branch merging when a leaf is just a null value // instead of being wrapped in an atom. // - If undefined then return null (previous behavior). // - If they're both branches, return the branch. // - If they're both edges, continue below. if (nodeValue === message) { // There should not be undefined values. Those should always be // wrapped in an $atom if (message === null) { node = wrapNode(message, undefined, message); parent = updateNodeAncestors(parent, -node.$size, lru, version); node = insertNode(node, parent, key, undefined, optimizedPath); return node; } // The messange and cache are both undefined, therefore return null. else if (message === undefined) { return message; } else { cIsObject = isObject(node); if (cIsObject) { // Is the cache node a branch? If so, return the cache branch. cType = node.$type; if (cType == null) { // Has the branch been introduced to the cache yet? If not, // give it a parent, key, and absolute path. if (node.$_parent == null) { insertNode(node, parent, key, version, optimizedPath); } return node; } } } } else { cIsObject = isObject(node); if (cIsObject) { cType = node.$type; } } // If the cache isn't a reference, we might be able to return early. if (cType !== $ref) { mIsObject = isObject(message); if (mIsObject) { mType = message.$type; } if (cIsObject && !cType) { // If the cache is a branch and the message is empty or // also a branch, continue with the cache branch. if (message == null || (mIsObject && !mType)) { return node; } } } // If the cache is a reference, we might not need to replace it. else { // If the cache is a reference, but the message is empty, leave the cache alone... if (message == null) { // ...unless the cache is an expired reference. In that case, expire // the cache node and return undefined. if (isExpired(node)) { expireNode(node, expired, lru); return void 0; } return node; } mIsObject = isObject(message); if (mIsObject) { mType = message.$type; // If the cache and the message are both references, // check if we need to replace the cache reference. if (mType === $ref) { if (node === message) { // If the cache and message are the same reference, // we performed a whole-branch merge of one of the // grandparents. If we've previously graphed this // reference, break early. Otherwise, continue to // leaf insertion below. if (node.$_parent != null) { return node; } } else { cTimestamp = node.$timestamp; mTimestamp = message.$timestamp; // - If either the cache or message reference is expired, // replace the cache reference with the message. // - If neither of the references are expired, compare their // timestamps. If either of them don't have a timestamp, // or the message's timestamp is newer, replace the cache // reference with the message reference. // - If the message reference is older than the cache // reference, short-circuit. if (!isExpired(node) && !isExpired(message) && mTimestamp < cTimestamp) { return void 0; } } } } } // If the cache is a leaf but the message is a branch, merge the branch over the leaf. if (cType && mIsObject && !mType) { return insertNode(replaceNode(node, message, parent, key, lru, replacedPaths), parent, key, undefined, optimizedPath); } // If the message is a sentinel or primitive, insert it into the cache. else if (mType || !mIsObject) { // If the cache and the message are the same value, we branch-merged one // of the message's ancestors. If this is the first time we've seen this // leaf, give the message a $size and $type, attach its graph pointers, // and update the cache sizes and versions. if (mType === $error && isFunction(errorSelector)) { message = errorSelector(reconstructPath(requestedPath, key), message); mType = message.$type || mType; } if (mType && node === message) { if (node.$_parent == null) { node = wrapNode(node, mType, node.value); parent = updateNodeAncestors(parent, -node.$size, lru, version); node = insertNode(node, parent, key, version, optimizedPath); } } // If the cache and message are different, the cache value is expired, // or the message is a primitive, replace the cache with the message value. // If the message is a sentinel, clone and maintain its type. // If the message is a primitive value, wrap it in an atom. else { var isDistinct = true; // If the cache is a branch, but the message is a leaf, replace the // cache branch with the message leaf. if ((cType && !isExpired(node)) || !cIsObject) { // Compare the current cache value with the new value. If either of // them don't have a timestamp, or the message's timestamp is newer, // replace the cache value with the message value. If a comparator // is specified, the comparator takes precedence over timestamps. // // Comparing either Number or undefined to undefined always results in false. isDistinct = (getTimestamp(message) < getTimestamp(node)) === false; // If at least one of the cache/message are sentinels, compare them. if (isDistinct && (cType || mType) && isFunction(comparator)) { isDistinct = !comparator(nodeValue, message, optimizedPath.slice(0, optimizedPath.index)); } } if (isDistinct) { message = wrapNode(message, mType, mType ? message.value : message); sizeOffset = getSize(node) - getSize(message); node = replaceNode(node, message, parent, key, lru, replacedPaths); parent = updateNodeAncestors(parent, sizeOffset, lru, version); node = insertNode(node, parent, key, version, optimizedPath); } } // Promote the message edge in the LRU. if (isExpired(node)) { expireNode(node, expired, lru); } } else if (node == null) { node = insertNode({}, parent, key, undefined, optimizedPath); } return node; }; },{"104":104,"106":106,"109":109,"110":110,"75":75,"77":77,"78":78,"82":82,"84":84,"85":85,"89":89,"96":96,"99":99}],93:[function(require,module,exports){ var $ref = require(110); var $error = require(109); var getType = require(79); var getSize = require(77); var getTimestamp = require(78); var isExpired = require(84); var isPrimitive = require(91); var isFunction = require(85); var wrapNode = require(106); var expireNode = require(75); var insertNode = require(82); var replaceNode = require(99); var updateNodeAncestors = require(104); var updateBackReferenceVersions = require(103); var reconstructPath = require(96); module.exports = function mergeValueOrInsertBranch( parent, node, key, value, branch, reference, requestedPath, optimizedPath, version, expired, lru, comparator, errorSelector, replacedPaths) { var type = getType(node, reference); if (branch || reference) { if (type && isExpired(node)) { type = "expired"; expireNode(node, expired, lru); } if ((type && type !== $ref) || isPrimitive(node)) { node = replaceNode(node, {}, parent, key, lru, replacedPaths); node = insertNode(node, parent, key, version, optimizedPath); node = updateBackReferenceVersions(node, version); } } else { var message = value; var mType = getType(message); // Compare the current cache value with the new value. If either of // them don't have a timestamp, or the message's timestamp is newer, // replace the cache value with the message value. If a comparator // is specified, the comparator takes precedence over timestamps. // // Comparing either Number or undefined to undefined always results in false. var isDistinct = (getTimestamp(message) < getTimestamp(node)) === false; // If at least one of the cache/message are sentinels, compare them. if ((type || mType) && isFunction(comparator)) { isDistinct = !comparator(node, message, optimizedPath.slice(0, optimizedPath.index)); } if (isDistinct) { if (mType === $error && isFunction(errorSelector)) { message = errorSelector(reconstructPath(requestedPath, key), message); mType = message.$type || mType; } message = wrapNode(message, mType, mType ? message.value : message); var sizeOffset = getSize(node) - getSize(message); node = replaceNode(node, message, parent, key, lru, replacedPaths); parent = updateNodeAncestors(parent, sizeOffset, lru, version); node = insertNode(node, parent, key, version, optimizedPath); } } return node; }; },{"103":103,"104":104,"106":106,"109":109,"110":110,"75":75,"77":77,"78":78,"79":79,"82":82,"84":84,"85":85,"91":91,"96":96,"99":99}],94:[function(require,module,exports){ module.exports = function noop() {}; },{}],95:[function(require,module,exports){ module.exports = Date.now; },{}],96:[function(require,module,exports){ /** * Reconstructs the path for the current key, from currentPath (requestedPath) * state maintained during set/merge walk operations. * * During the walk, since the requestedPath array is updated after we attempt to * merge/insert nodes during a walk (it reflects the inserted node's parent branch) * we need to reconstitute a path from it. * * @param {Array} currentPath The current requestedPath state, during the walk * @param {String} key The current key value, during the walk * @return {Array} A new array, with the path which represents the node we're about * to insert */ module.exports = function reconstructPath(currentPath, key) { var path = currentPath.slice(0, currentPath.depth); path[path.length] = key; return path; }; },{}],97:[function(require,module,exports){ var $ref = require(110); var splice = require(41); var isObject = require(89); var unlinkBackReferences = require(101); var unlinkForwardReference = require(102); module.exports = function removeNode(node, parent, key, lru) { if (isObject(node)) { var type = node.$type; if (type) { if (type === $ref) { unlinkForwardReference(node); } splice(lru, node); } unlinkBackReferences(node); // eslint-disable-next-line camelcase parent[key] = node.$_parent = void 0; return true; } return false; }; },{"101":101,"102":102,"110":110,"41":41,"89":89}],98:[function(require,module,exports){ var hasOwn = require(80); var prefix = require(36); var removeNode = require(97); module.exports = function removeNodeAndDescendants(node, parent, key, lru, mergeContext) { if (removeNode(node, parent, key, lru)) { if (node.$type !== undefined && mergeContext && node.$_absolutePath) { mergeContext.hasInvalidatedResult = true; } if (node.$type == null) { for (var key2 in node) { if (key2[0] !== prefix && hasOwn(node, key2)) { removeNodeAndDescendants(node[key2], node, key2, lru, mergeContext); } } } return true; } return false; }; },{"36":36,"80":80,"97":97}],99:[function(require,module,exports){ var isObject = require(89); var transferBackReferences = require(100); var removeNodeAndDescendants = require(98); module.exports = function replaceNode(node, replacement, parent, key, lru, mergeContext) { if (node === replacement) { return node; } else if (isObject(node)) { transferBackReferences(node, replacement); removeNodeAndDescendants(node, parent, key, lru, mergeContext); } parent[key] = replacement; return replacement; }; },{"100":100,"89":89,"98":98}],100:[function(require,module,exports){ var __ref = require(35); module.exports = function transferBackReferences(fromNode, destNode) { // eslint-disable-next-line camelcase var fromNodeRefsLength = fromNode.$_refsLength || 0, // eslint-disable-next-line camelcase destNodeRefsLength = destNode.$_refsLength || 0, i = -1; while (++i < fromNodeRefsLength) { var ref = fromNode[__ref + i]; if (ref !== void 0) { // eslint-disable-next-line camelcase ref.$_context = destNode; destNode[__ref + (destNodeRefsLength + i)] = ref; fromNode[__ref + i] = void 0; } } // eslint-disable-next-line camelcase destNode.$_refsLength = fromNodeRefsLength + destNodeRefsLength; // eslint-disable-next-line camelcase fromNode.$_refsLength = void 0; return destNode; }; },{"35":35}],101:[function(require,module,exports){ var __ref = require(35); module.exports = function unlinkBackReferences(node) { // eslint-disable-next-line camelcase var i = -1, n = node.$_refsLength || 0; while (++i < n) { var ref = node[__ref + i]; if (ref != null) { // eslint-disable-next-line camelcase ref.$_context = ref.$_refIndex = node[__ref + i] = void 0; } } // eslint-disable-next-line camelcase node.$_refsLength = void 0; return node; }; },{"35":35}],102:[function(require,module,exports){ var __ref = require(35); module.exports = function unlinkForwardReference(reference) { // eslint-disable-next-line camelcase var destination = reference.$_context; if (destination) { // eslint-disable-next-line camelcase var i = (reference.$_refIndex || 0) - 1, // eslint-disable-next-line camelcase n = (destination.$_refsLength || 0) - 1; while (++i <= n) { destination[__ref + i] = destination[__ref + (i + 1)]; } // eslint-disable-next-line camelcase destination.$_refsLength = n; // eslint-disable-next-line camelcase reference.$_refIndex = reference.$_context = destination = void 0; } return reference; }; },{"35":35}],103:[function(require,module,exports){ var __ref = require(35); module.exports = function updateBackReferenceVersions(nodeArg, version) { var stack = [nodeArg]; var count = 0; do { var node = stack[count]; // eslint-disable-next-line camelcase if (node && node.$_version !== version) { // eslint-disable-next-line camelcase node.$_version = version; // eslint-disable-next-line camelcase stack[count++] = node.$_parent; var i = -1; // eslint-disable-next-line camelcase var n = node.$_refsLength || 0; while (++i < n) { stack[count++] = node[__ref + i]; } } } while (--count > -1); return nodeArg; }; },{"35":35}],104:[function(require,module,exports){ var removeNode = require(97); var updateBackReferenceVersions = require(103); module.exports = function updateNodeAncestors(nodeArg, offset, lru, version) { var child = nodeArg; do { var node = child.$_parent; var size = child.$size = (child.$size || 0) - offset; if (size <= 0 && node != null) { removeNode(child, node, child.$_key, lru); } else if (child.$_version !== version) { updateBackReferenceVersions(child, version); } child = node; } while (child); return nodeArg; }; },{"103":103,"97":97}],105:[function(require,module,exports){ var isArray = Array.isArray; var isPathValue = require(90); var isJSONGraphEnvelope = require(88); var isJSONEnvelope = require(87); var pathSyntax = require(124); /** * * @param {Object} allowedInput - allowedInput is a map of input styles * that are allowed * @private */ module.exports = function validateInput(args, allowedInput, method) { for (var i = 0, len = args.length; i < len; ++i) { var arg = args[i]; var valid = false; // Path if (isArray(arg) && allowedInput.path) { valid = true; } // Path Syntax else if (typeof arg === "string" && allowedInput.pathSyntax) { try { pathSyntax.fromPath(arg); valid = true; } catch (errorMessage) { return new Error("Path syntax validation error -- " + errorMessage); } } // Path Value else if (isPathValue(arg) && allowedInput.pathValue) { try { arg.path = pathSyntax.fromPath(arg.path); valid = true; } catch (errorMessage) { return new Error("Path syntax validation error -- " + errorMessage); } } // jsonGraph {jsonGraph: { ... }, paths: [ ... ]} else if (isJSONGraphEnvelope(arg) && allowedInput.jsonGraph) { valid = true; } // json env {json: {...}} else if (isJSONEnvelope(arg) && allowedInput.json) { valid = true; } // selector functions else if (typeof arg === "function" && i + 1 === len && allowedInput.selector) { valid = true; } if (!valid) { return new Error("Unrecognized argument " + (typeof arg) + " [" + String(arg) + "] " + "to Model#" + method + ""); } } return true; }; },{"124":124,"87":87,"88":88,"90":90}],106:[function(require,module,exports){ var now = require(95); var expiresNow = require(112); var atomSize = 50; var clone = require(72); var isArray = Array.isArray; var getSize = require(77); var getExpires = require(76); var atomType = require(108); module.exports = function wrapNode(nodeArg, typeArg, value) { var size = 0; var node = nodeArg; var type = typeArg; if (type) { var modelCreated = node.$_modelCreated; node = clone(node); size = getSize(node); node.$type = type; // eslint-disable-next-line camelcase node.$_prev = undefined; // eslint-disable-next-line camelcase node.$_next = undefined; // eslint-disable-next-line camelcase node.$_modelCreated = modelCreated || false; } else { node = { $type: atomType, value: value, // eslint-disable-next-line camelcase $_prev: undefined, // eslint-disable-next-line camelcase $_next: undefined, // eslint-disable-next-line camelcase $_modelCreated: true }; } if (value == null) { size = atomSize + 1; } else if (size == null || size <= 0) { switch (typeof value) { case "object": if (isArray(value)) { size = atomSize + value.length; } else { size = atomSize + 1; } break; case "string": size = atomSize + value.length; break; default: size = atomSize + 1; break; } } var expires = getExpires(node); if (typeof expires === "number" && expires < expiresNow) { node.$expires = now() + (expires * -1); } node.$size = size; return node; }; },{"108":108,"112":112,"72":72,"76":76,"77":77,"95":95}],107:[function(require,module,exports){ /** * FromEsObserverAdapter is an adpater from an ES Observer to an Rx 2 Observer * @constructor FromEsObserverAdapter */ function FromEsObserverAdapter(esObserver) { this.esObserver = esObserver; } FromEsObserverAdapter.prototype = { onNext: function onNext(value) { if (typeof this.esObserver.next === "function") { this.esObserver.next(value); } }, onError: function onError(error) { if (typeof this.esObserver.error === "function") { this.esObserver.error(error); } }, onCompleted: function onCompleted() { if (typeof this.esObserver.complete === "function") { this.esObserver.complete(); } } }; /** * ToEsSubscriptionAdapter is an adpater from the Rx 2 subscription to the ES subscription * @constructor ToEsSubscriptionAdapter */ function ToEsSubscriptionAdapter(subscription) { this.subscription = subscription; } ToEsSubscriptionAdapter.prototype.unsubscribe = function unsubscribe() { this.subscription.dispose(); }; function toEsObservable(_self) { return { subscribe: function subscribe(observer) { return new ToEsSubscriptionAdapter(_self.subscribe(new FromEsObserverAdapter(observer))); } }; } module.exports = toEsObservable; },{}],108:[function(require,module,exports){ module.exports = "atom"; },{}],109:[function(require,module,exports){ module.exports = "error"; },{}],110:[function(require,module,exports){ module.exports = "ref"; },{}],111:[function(require,module,exports){ module.exports = 1; },{}],112:[function(require,module,exports){ module.exports = 0; },{}],113:[function(require,module,exports){ "use strict"; // rawAsap provides everything we need except exception management. var rawAsap = require(114); // RawTasks are recycled to reduce GC churn. var freeTasks = []; // We queue errors to ensure they are thrown in right order (FIFO). // Array-as-queue is good enough here, since we are just dealing with exceptions. var pendingErrors = []; var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError); function throwFirstError() { if (pendingErrors.length) { throw pendingErrors.shift(); } } /** * Calls a task as soon as possible after returning, in its own event, with priority * over other events like animation, reflow, and repaint. An error thrown from an * event will not interrupt, nor even substantially slow down the processing of * other events, but will be rather postponed to a lower priority event. * @param {{call}} task A callable object, typically a function that takes no * arguments. */ module.exports = asap; function asap(task) { var rawTask; if (freeTasks.length) { rawTask = freeTasks.pop(); } else { rawTask = new RawTask(); } rawTask.task = task; rawAsap(rawTask); } // We wrap tasks with recyclable task objects. A task object implements // `call`, just like a function. function RawTask() { this.task = null; } // The sole purpose of wrapping the task is to catch the exception and recycle // the task object after its single use. RawTask.prototype.call = function () { try { this.task.call(); } catch (error) { if (asap.onerror) { // This hook exists purely for testing purposes. // Its name will be periodically randomized to break any code that // depends on its existence. asap.onerror(error); } else { // In a web browser, exceptions are not fatal. However, to avoid // slowing down the queue of pending tasks, we rethrow the error in a // lower priority turn. pendingErrors.push(error); requestErrorThrow(); } } finally { this.task = null; freeTasks[freeTasks.length] = this; } }; },{"114":114}],114:[function(require,module,exports){ (function (global){(function (){ "use strict"; // Use the fastest means possible to execute a task in its own turn, with // priority over other events including IO, animation, reflow, and redraw // events in browsers. // // An exception thrown by a task will permanently interrupt the processing of // subsequent tasks. The higher level `asap` function ensures that if an // exception is thrown by a task, that the task queue will continue flushing as // soon as possible, but if you use `rawAsap` directly, you are responsible to // either ensure that no exceptions are thrown from your task, or to manually // call `rawAsap.requestFlush` if an exception is thrown. module.exports = rawAsap; function rawAsap(task) { if (!queue.length) { requestFlush(); flushing = true; } // Equivalent to push, but avoids a function call. queue[queue.length] = task; } var queue = []; // Once a flush has been requested, no further calls to `requestFlush` are // necessary until the next `flush` completes. var flushing = false; // `requestFlush` is an implementation-specific method that attempts to kick // off a `flush` event as quickly as possible. `flush` will attempt to exhaust // the event queue before yielding to the browser's own event loop. var requestFlush; // The position of the next task to execute in the task queue. This is // preserved between calls to `flush` so that it can be resumed if // a task throws an exception. var index = 0; // If a task schedules additional tasks recursively, the task queue can grow // unbounded. To prevent memory exhaustion, the task queue will periodically // truncate already-completed tasks. var capacity = 1024; // The flush function processes all tasks that have been scheduled with // `rawAsap` unless and until one of those tasks throws an exception. // If a task throws an exception, `flush` ensures that its state will remain // consistent and will resume where it left off when called again. // However, `flush` does not make any arrangements to be called again if an // exception is thrown. function flush() { while (index < queue.length) { var currentIndex = index; // Advance the index before calling the task. This ensures that we will // begin flushing on the next task the task throws an error. index = index + 1; queue[currentIndex].call(); // Prevent leaking memory for long chains of recursive calls to `asap`. // If we call `asap` within tasks scheduled by `asap`, the queue will // grow, but to avoid an O(n) walk for every task we execute, we don't // shift tasks off the queue after they have been executed. // Instead, we periodically shift 1024 tasks off the queue. if (index > capacity) { // Manually shift all values starting at the index back to the // beginning of the queue. for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) { queue[scan] = queue[scan + index]; } queue.length -= index; index = 0; } } queue.length = 0; index = 0; flushing = false; } // `requestFlush` is implemented using a strategy based on data collected from // every available SauceLabs Selenium web driver worker at time of writing. // https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593 // Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that // have WebKitMutationObserver but not un-prefixed MutationObserver. // Must use `global` or `self` instead of `window` to work in both frames and web // workers. `global` is a provision of Browserify, Mr, Mrs, or Mop. /* globals self */ var scope = typeof global !== "undefined" ? global : self; var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; // MutationObservers are desirable because they have high priority and work // reliably everywhere they are implemented. // They are implemented in all modern browsers. // // - Android 4-4.3 // - Chrome 26-34 // - Firefox 14-29 // - Internet Explorer 11 // - iPad Safari 6-7.1 // - iPhone Safari 7-7.1 // - Safari 6-7 if (typeof BrowserMutationObserver === "function") { requestFlush = makeRequestCallFromMutationObserver(flush); // MessageChannels are desirable because they give direct access to the HTML // task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera // 11-12, and in web workers in many engines. // Although message channels yield to any queued rendering and IO tasks, they // would be better than imposing the 4ms delay of timers. // However, they do not work reliably in Internet Explorer or Safari. // Internet Explorer 10 is the only browser that has setImmediate but does // not have MutationObservers. // Although setImmediate yields to the browser's renderer, it would be // preferrable to falling back to setTimeout since it does not have // the minimum 4ms penalty. // Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and // Desktop to a lesser extent) that renders both setImmediate and // MessageChannel useless for the purposes of ASAP. // https://github.com/kriskowal/q/issues/396 // Timers are implemented universally. // We fall back to timers in workers in most engines, and in foreground // contexts in the following browsers. // However, note that even this simple case requires nuances to operate in a // broad spectrum of browsers. // // - Firefox 3-13 // - Internet Explorer 6-9 // - iPad Safari 4.3 // - Lynx 2.8.7 } else { requestFlush = makeRequestCallFromTimer(flush); } // `requestFlush` requests that the high priority event queue be flushed as // soon as possible. // This is useful to prevent an error thrown in a task from stalling the event // queue if the exception handled by Node.js’s // `process.on("uncaughtException")` or by a domain. rawAsap.requestFlush = requestFlush; // To request a high priority event, we induce a mutation observer by toggling // the text of a text node between "1" and "-1". function makeRequestCallFromMutationObserver(callback) { var toggle = 1; var observer = new BrowserMutationObserver(callback); var node = document.createTextNode(""); observer.observe(node, {characterData: true}); return function requestCall() { toggle = -toggle; node.data = toggle; }; } // The message channel technique was discovered by Malte Ubl and was the // original foundation for this library. // http://www.nonblocking.io/2011/06/windownexttick.html // Safari 6.0.5 (at least) intermittently fails to create message ports on a // page's first load. Thankfully, this version of Safari supports // MutationObservers, so we don't need to fall back in that case. // function makeRequestCallFromMessageChannel(callback) { // var channel = new MessageChannel(); // channel.port1.onmessage = callback; // return function requestCall() { // channel.port2.postMessage(0); // }; // } // For reasons explained above, we are also unable to use `setImmediate` // under any circumstances. // Even if we were, there is another bug in Internet Explorer 10. // It is not sufficient to assign `setImmediate` to `requestFlush` because // `setImmediate` must be called *by name* and therefore must be wrapped in a // closure. // Never forget. // function makeRequestCallFromSetImmediate(callback) { // return function requestCall() { // setImmediate(callback); // }; // } // Safari 6.0 has a problem where timers will get lost while the user is // scrolling. This problem does not impact ASAP because Safari 6.0 supports // mutation observers, so that implementation is used instead. // However, if we ever elect to use timers in Safari, the prevalent work-around // is to add a scroll event listener that calls for a flush. // `setTimeout` does not call the passed callback if the delay is less than // approximately 7 in web workers in Firefox 8 through 18, and sometimes not // even then. function makeRequestCallFromTimer(callback) { return function requestCall() { // We dispatch a timeout with a specified delay of 0 for engines that // can reliably accommodate that request. This will usually be snapped // to a 4 milisecond delay, but once we're flushing, there's no delay // between events. var timeoutHandle = setTimeout(handleTimer, 0); // However, since this timer gets frequently dropped in Firefox // workers, we enlist an interval handle that will try to fire // an event 20 times per second until it succeeds. var intervalHandle = setInterval(handleTimer, 50); function handleTimer() { // Whichever timer succeeds will cancel both timers and // execute the callback. clearTimeout(timeoutHandle); clearInterval(intervalHandle); callback(); } }; } // This is for `asap.js` only. // Its name will be periodically randomized to break any code that depends on // its existence. rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer; // ASAP was originally a nextTick shim included in Q. This was factored out // into this ASAP package. It was later adapted to RSVP which made further // amendments. These decisions, particularly to marginalize MessageChannel and // to capture the MutationObserver implementation in a closure, were integrated // back into ASAP proper. // https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],115:[function(require,module,exports){ 'use strict'; var request = require(119); var buildQueryObject = require(116); var isArray = Array.isArray; function simpleExtend(obj, obj2) { var prop; for (prop in obj2) { obj[prop] = obj2[prop]; } return obj; } function XMLHttpSource(jsongUrl, config) { this._jsongUrl = jsongUrl; if (typeof config === 'number') { var newConfig = { timeout: config }; config = newConfig; } this._config = simpleExtend({ timeout: 15000, headers: {} }, config || {}); } XMLHttpSource.prototype = { // because javascript constructor: XMLHttpSource, /** * buildQueryObject helper */ buildQueryObject: buildQueryObject, /** * @inheritDoc DataSource#get */ get: function httpSourceGet(pathSet) { var method = 'GET'; var queryObject = this.buildQueryObject(this._jsongUrl, method, { paths: pathSet, method: 'get' }); var config = simpleExtend(queryObject, this._config); // pass context for onBeforeRequest callback var context = this; return request(method, config, context); }, /** * @inheritDoc DataSource#set */ set: function httpSourceSet(jsongEnv) { var method = 'POST'; var queryObject = this.buildQueryObject(this._jsongUrl, method, { jsonGraph: jsongEnv, method: 'set' }); var config = simpleExtend(queryObject, this._config); config.headers["Content-Type"] = "application/x-www-form-urlencoded"; // pass context for onBeforeRequest callback var context = this; return request(method, config, context); }, /** * @inheritDoc DataSource#call */ call: function httpSourceCall(callPath, args, pathSuffix, paths) { // arguments defaults args = args || []; pathSuffix = pathSuffix || []; paths = paths || []; var method = 'POST'; var queryData = []; queryData.push('method=call'); queryData.push('callPath=' + encodeURIComponent(JSON.stringify(callPath))); queryData.push('arguments=' + encodeURIComponent(JSON.stringify(args))); queryData.push('pathSuffixes=' + encodeURIComponent(JSON.stringify(pathSuffix))); queryData.push('paths=' + encodeURIComponent(JSON.stringify(paths))); var queryObject = this.buildQueryObject(this._jsongUrl, method, queryData.join('&')); var config = simpleExtend(queryObject, this._config); config.headers["Content-Type"] = "application/x-www-form-urlencoded"; // pass context for onBeforeRequest callback var context = this; return request(method, config, context); } }; // ES6 modules XMLHttpSource.XMLHttpSource = XMLHttpSource; XMLHttpSource['default'] = XMLHttpSource; // commonjs module.exports = XMLHttpSource; },{"116":116,"119":119}],116:[function(require,module,exports){ 'use strict'; module.exports = function buildQueryObject(url, method, queryData) { var qData = []; var keys; var data = {url: url}; var isQueryParamUrl = url.indexOf('?') !== -1; var startUrl = (isQueryParamUrl) ? '&' : '?'; if (typeof queryData === 'string') { qData.push(queryData); } else { keys = Object.keys(queryData); keys.forEach(function (k) { var value = (typeof queryData[k] === 'object') ? JSON.stringify(queryData[k]) : queryData[k]; qData.push(k + '=' + encodeURIComponent(value)); }); } if (method === 'GET') { data.url += startUrl + qData.join('&'); } else { data.data = qData.join('&'); } return data; }; },{}],117:[function(require,module,exports){ (function (global){(function (){ 'use strict'; // Get CORS support even for older IE module.exports = function getCORSRequest() { var xhr = new global.XMLHttpRequest(); if ('withCredentials' in xhr) { return xhr; } else if (!!global.XDomainRequest) { return new XDomainRequest(); } else { throw new Error('CORS is not supported by your browser'); } }; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],118:[function(require,module,exports){ (function (global){(function (){ 'use strict'; module.exports = function getXMLHttpRequest() { var progId, progIds, i; if (global.XMLHttpRequest) { return new global.XMLHttpRequest(); } else { try { progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; for (i = 0; i < 3; i++) { try { progId = progIds[i]; if (new global.ActiveXObject(progId)) { break; } } catch(e) { } } return new global.ActiveXObject(progId); } catch (e) { throw new Error('XMLHttpRequest is not supported by your browser'); } } }; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],119:[function(require,module,exports){ 'use strict'; var getXMLHttpRequest = require(118); var getCORSRequest = require(117); var hasOwnProp = Object.prototype.hasOwnProperty; var noop = function() {}; function Observable() {} Observable.create = function(subscribe) { var o = new Observable(); o.subscribe = function(onNext, onError, onCompleted) { var observer; var disposable; if (typeof onNext === 'function') { observer = { onNext: onNext, onError: (onError || noop), onCompleted: (onCompleted || noop) }; } else { observer = onNext; } disposable = subscribe(observer); if (typeof disposable === 'function') { return { dispose: disposable }; } else { return disposable; } }; return o; }; function request(method, options, context) { return Observable.create(function requestObserver(observer) { var config = { method: method || 'GET', crossDomain: false, async: true, headers: {}, responseType: 'json' }; var xhr, isDone, headers, header, prop; for (prop in options) { if (hasOwnProp.call(options, prop)) { config[prop] = options[prop]; } } // Add request with Headers if (!config.crossDomain && !config.headers['X-Requested-With']) { config.headers['X-Requested-With'] = 'XMLHttpRequest'; } // allow the user to mutate the config open if (context.onBeforeRequest != null) { context.onBeforeRequest(config); } // create xhr try { xhr = config.crossDomain ? getCORSRequest() : getXMLHttpRequest(); } catch (err) { observer.onError(err); } try { // Takes the url and opens the connection if (config.user) { xhr.open(config.method, config.url, config.async, config.user, config.password); } else { xhr.open(config.method, config.url, config.async); } // Sets timeout information xhr.timeout = config.timeout; // Anything but explicit false results in true. xhr.withCredentials = config.withCredentials !== false; // Fills the request headers headers = config.headers; for (header in headers) { if (hasOwnProp.call(headers, header)) { xhr.setRequestHeader(header, headers[header]); } } if (config.responseType) { try { xhr.responseType = config.responseType; } catch (e) { // WebKit added support for the json responseType value on 09/03/2013 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are // known to throw when setting the value "json" as the response type. Other older // browsers implementing the responseType // // The json response type can be ignored if not supported, because JSON payloads are // parsed on the client-side regardless. if (config.responseType !== 'json') { throw e; } } } xhr.onreadystatechange = function onreadystatechange(e) { // Complete if (xhr.readyState === 4) { if (!isDone) { isDone = true; onXhrLoad(observer, xhr, e); } } }; // Timeout xhr.ontimeout = function ontimeout(e) { if (!isDone) { isDone = true; onXhrError(observer, xhr, 'timeout error', e); } }; // Send Request xhr.send(config.data); } catch (e) { observer.onError(e); } // Dispose return function dispose() { // Doesn't work in IE9 if (!isDone && xhr.readyState !== 4) { isDone = true; xhr.abort(); } };//Dispose }); } /* * General handling of ultimate failure (after appropriate retries) */ function _handleXhrError(observer, textStatus, errorThrown) { // IE9: cross-domain request may be considered errors if (!errorThrown) { errorThrown = new Error(textStatus); } observer.onError(errorThrown); } function onXhrLoad(observer, xhr, e) { var responseData, responseObject, responseType; // If there's no observer, the request has been (or is being) cancelled. if (xhr && observer) { responseType = xhr.responseType; // responseText is the old-school way of retrieving response (supported by IE8 & 9) // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) responseData = ('response' in xhr) ? xhr.response : xhr.responseText; // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) var status = (xhr.status === 1223) ? 204 : xhr.status; if (status >= 200 && status <= 399) { try { if (responseType !== 'json') { responseData = JSON.parse(responseData || ''); } if (typeof responseData === 'string') { responseData = JSON.parse(responseData || ''); } } catch (e) { _handleXhrError(observer, 'invalid json', e); } observer.onNext(responseData); observer.onCompleted(); return; } else if (status === 401 || status === 403 || status === 407) { return _handleXhrError(observer, responseData); } else if (status === 410) { // TODO: Retry ? return _handleXhrError(observer, responseData); } else if (status === 408 || status === 504) { // TODO: Retry ? return _handleXhrError(observer, responseData); } else { return _handleXhrError(observer, responseData || ('Response code ' + status)); }//if }//if }//onXhrLoad function onXhrError(observer, xhr, status, e) { _handleXhrError(observer, status || xhr.statusText || 'request error', e); } module.exports = request; },{"117":117,"118":118}],120:[function(require,module,exports){ var pathSyntax = require(124); function sentinel(type, value, props) { var copy = Object.create(null); if (props != null) { for(var key in props) { copy[key] = props[key]; } copy["$type"] = type; copy.value = value; return copy; } else { return { $type: type, value: value }; } } module.exports = { ref: function ref(path, props) { return sentinel("ref", pathSyntax.fromPath(path), props); }, atom: function atom(value, props) { return sentinel("atom", value, props); }, undefined: function() { return sentinel("atom"); }, error: function error(errorValue, props) { return sentinel("error", errorValue, props); }, pathValue: function pathValue(path, value) { return { path: pathSyntax.fromPath(path), value: value }; }, pathInvalidation: function pathInvalidation(path) { return { path: pathSyntax.fromPath(path), invalidated: true }; } }; },{"124":124}],121:[function(require,module,exports){ module.exports = { integers: 'integers', ranges: 'ranges', keys: 'keys' }; },{}],122:[function(require,module,exports){ var TokenTypes = { token: 'token', dotSeparator: '.', commaSeparator: ',', openingBracket: '[', closingBracket: ']', openingBrace: '{', closingBrace: '}', escape: '\\', space: ' ', colon: ':', quote: 'quote', unknown: 'unknown' }; module.exports = TokenTypes; },{}],123:[function(require,module,exports){ module.exports = { indexer: { nested: 'Indexers cannot be nested.', needQuotes: 'unquoted indexers must be numeric.', empty: 'cannot have empty indexers.', leadingDot: 'Indexers cannot have leading dots.', leadingComma: 'Indexers cannot have leading comma.', requiresComma: 'Indexers require commas between indexer args.', routedTokens: 'Only one token can be used per indexer when specifying routed tokens.' }, range: { precedingNaN: 'ranges must be preceded by numbers.', suceedingNaN: 'ranges must be suceeded by numbers.' }, routed: { invalid: 'Invalid routed token. only integers|ranges|keys are supported.' }, quote: { empty: 'cannot have empty quoted keys.', illegalEscape: 'Invalid escape character. Only quotes are escapable.' }, unexpectedToken: 'Unexpected token.', invalidIdentifier: 'Invalid Identifier.', invalidPath: 'Please provide a valid path.', throwError: function(err, tokenizer, token) { if (token) { throw err + ' -- ' + tokenizer.parseString + ' with next token: ' + token; } throw err + ' -- ' + tokenizer.parseString; } }; },{}],124:[function(require,module,exports){ var Tokenizer = require(130); var head = require(125); var RoutedTokens = require(121); var parser = function parser(string, extendedRules) { return head(new Tokenizer(string, extendedRules)); }; module.exports = parser; // Constructs the paths from paths / pathValues that have strings. // If it does not have a string, just moves the value into the return // results. parser.fromPathsOrPathValues = function(paths, ext) { if (!paths) { return []; } var out = []; for (var i = 0, len = paths.length; i < len; i++) { // Is the path a string if (typeof paths[i] === 'string') { out[i] = parser(paths[i], ext); } // is the path a path value with a string value. else if (typeof paths[i].path === 'string') { out[i] = { path: parser(paths[i].path, ext), value: paths[i].value }; } // just copy it over. else { out[i] = paths[i]; } } return out; }; // If the argument is a string, this with convert, else just return // the path provided. parser.fromPath = function(path, ext) { if (!path) { return []; } if (typeof path === 'string') { return parser(path, ext); } return path; }; // Potential routed tokens. parser.RoutedTokens = RoutedTokens; },{"121":121,"125":125,"130":130}],125:[function(require,module,exports){ var TokenTypes = require(122); var E = require(123); var indexer = require(126); /** * The top level of the parse tree. This returns the generated path * from the tokenizer. */ module.exports = function head(tokenizer) { var token = tokenizer.next(); var state = {}; var out = []; while (!token.done) { switch (token.type) { case TokenTypes.token: var first = +token.token[0]; if (!isNaN(first)) { E.throwError(E.invalidIdentifier, tokenizer); } out[out.length] = token.token; break; // dotSeparators at the top level have no meaning case TokenTypes.dotSeparator: if (out.length === 0) { E.throwError(E.unexpectedToken, tokenizer); } break; // Spaces do nothing. case TokenTypes.space: // NOTE: Spaces at the top level are allowed. // titlesById .summary is a valid path. break; // Its time to decend the parse tree. case TokenTypes.openingBracket: indexer(tokenizer, token, state, out); break; default: E.throwError(E.unexpectedToken, tokenizer); break; } // Keep cycling through the tokenizer. token = tokenizer.next(); } if (out.length === 0) { E.throwError(E.invalidPath, tokenizer); } return out; }; },{"122":122,"123":123,"126":126}],126:[function(require,module,exports){ var TokenTypes = require(122); var E = require(123); var idxE = E.indexer; var range = require(128); var quote = require(127); var routed = require(129); /** * The indexer is all the logic that happens in between * the '[', opening bracket, and ']' closing bracket. */ module.exports = function indexer(tokenizer, openingToken, state, out) { var token = tokenizer.next(); var done = false; var allowedMaxLength = 1; var routedIndexer = false; // State variables state.indexer = []; while (!token.done) { switch (token.type) { case TokenTypes.token: case TokenTypes.quote: // ensures that token adders are properly delimited. if (state.indexer.length === allowedMaxLength) { E.throwError(idxE.requiresComma, tokenizer); } break; } switch (token.type) { // Extended syntax case case TokenTypes.openingBrace: routedIndexer = true; routed(tokenizer, token, state, out); break; case TokenTypes.token: var t = +token.token; if (isNaN(t)) { E.throwError(idxE.needQuotes, tokenizer); } state.indexer[state.indexer.length] = t; break; // dotSeparators at the top level have no meaning case TokenTypes.dotSeparator: if (!state.indexer.length) { E.throwError(idxE.leadingDot, tokenizer); } range(tokenizer, token, state, out); break; // Spaces do nothing. case TokenTypes.space: break; case TokenTypes.closingBracket: done = true; break; // The quotes require their own tree due to what can be in it. case TokenTypes.quote: quote(tokenizer, token, state, out); break; // Its time to decend the parse tree. case TokenTypes.openingBracket: E.throwError(idxE.nested, tokenizer); break; case TokenTypes.commaSeparator: ++allowedMaxLength; break; default: E.throwError(E.unexpectedToken, tokenizer); break; } // If done, leave loop if (done) { break; } // Keep cycling through the tokenizer. token = tokenizer.next(); } if (state.indexer.length === 0) { E.throwError(idxE.empty, tokenizer); } if (state.indexer.length > 1 && routedIndexer) { E.throwError(idxE.routedTokens, tokenizer); } // Remember, if an array of 1, keySets will be generated. if (state.indexer.length === 1) { state.indexer = state.indexer[0]; } out[out.length] = state.indexer; // Clean state. state.indexer = undefined; }; },{"122":122,"123":123,"127":127,"128":128,"129":129}],127:[function(require,module,exports){ var TokenTypes = require(122); var E = require(123); var quoteE = E.quote; /** * quote is all the parse tree in between quotes. This includes the only * escaping logic. * * parse-tree: * (.|())* */ module.exports = function quote(tokenizer, openingToken, state, out) { var token = tokenizer.next(); var innerToken = ''; var openingQuote = openingToken.token; var escaping = false; var done = false; while (!token.done) { switch (token.type) { case TokenTypes.token: case TokenTypes.space: case TokenTypes.dotSeparator: case TokenTypes.commaSeparator: case TokenTypes.openingBracket: case TokenTypes.closingBracket: case TokenTypes.openingBrace: case TokenTypes.closingBrace: if (escaping) { E.throwError(quoteE.illegalEscape, tokenizer); } innerToken += token.token; break; case TokenTypes.quote: // the simple case. We are escaping if (escaping) { innerToken += token.token; escaping = false; } // its not a quote that is the opening quote else if (token.token !== openingQuote) { innerToken += token.token; } // last thing left. Its a quote that is the opening quote // therefore we must produce the inner token of the indexer. else { done = true; } break; case TokenTypes.escape: escaping = true; break; default: E.throwError(E.unexpectedToken, tokenizer); } // If done, leave loop if (done) { break; } // Keep cycling through the tokenizer. token = tokenizer.next(); } if (innerToken.length === 0) { E.throwError(quoteE.empty, tokenizer); } state.indexer[state.indexer.length] = innerToken; }; },{"122":122,"123":123}],128:[function(require,module,exports){ var Tokenizer = require(130); var TokenTypes = require(122); var E = require(123); /** * The indexer is all the logic that happens in between * the '[', opening bracket, and ']' closing bracket. */ module.exports = function range(tokenizer, openingToken, state, out) { var token = tokenizer.peek(); var dotCount = 1; var done = false; var inclusive = true; // Grab the last token off the stack. Must be an integer. var idx = state.indexer.length - 1; var from = Tokenizer.toNumber(state.indexer[idx]); var to; if (isNaN(from)) { E.throwError(E.range.precedingNaN, tokenizer); } // Why is number checking so difficult in javascript. while (!done && !token.done) { switch (token.type) { // dotSeparators at the top level have no meaning case TokenTypes.dotSeparator: if (dotCount === 3) { E.throwError(E.unexpectedToken, tokenizer); } ++dotCount; if (dotCount === 3) { inclusive = false; } break; case TokenTypes.token: // move the tokenizer forward and save to. to = Tokenizer.toNumber(tokenizer.next().token); // throw potential error. if (isNaN(to)) { E.throwError(E.range.suceedingNaN, tokenizer); } done = true; break; default: done = true; break; } // Keep cycling through the tokenizer. But ranges have to peek // before they go to the next token since there is no 'terminating' // character. if (!done) { tokenizer.next(); // go to the next token without consuming. token = tokenizer.peek(); } // break and remove state information. else { break; } } state.indexer[idx] = {from: from, to: inclusive ? to : to - 1}; }; },{"122":122,"123":123,"130":130}],129:[function(require,module,exports){ var TokenTypes = require(122); var RoutedTokens = require(121); var E = require(123); var routedE = E.routed; /** * The routing logic. * * parse-tree: * (:) */ module.exports = function routed(tokenizer, openingToken, state, out) { var routeToken = tokenizer.next(); var named = false; var name = ''; // ensure the routed token is a valid ident. switch (routeToken.token) { case RoutedTokens.integers: case RoutedTokens.ranges: case RoutedTokens.keys: //valid break; default: E.throwError(routedE.invalid, tokenizer); break; } // Now its time for colon or ending brace. var next = tokenizer.next(); // we are parsing a named identifier. if (next.type === TokenTypes.colon) { named = true; // Get the token name. next = tokenizer.next(); if (next.type !== TokenTypes.token) { E.throwError(routedE.invalid, tokenizer); } name = next.token; // move to the closing brace. next = tokenizer.next(); } // must close with a brace. if (next.type === TokenTypes.closingBrace) { var outputToken = { type: routeToken.token, named: named, name: name }; state.indexer[state.indexer.length] = outputToken; } // closing brace expected else { E.throwError(routedE.invalid, tokenizer); } }; },{"121":121,"122":122,"123":123}],130:[function(require,module,exports){ var TokenTypes = require(122); var DOT_SEPARATOR = '.'; var COMMA_SEPARATOR = ','; var OPENING_BRACKET = '['; var CLOSING_BRACKET = ']'; var OPENING_BRACE = '{'; var CLOSING_BRACE = '}'; var COLON = ':'; var ESCAPE = '\\'; var DOUBLE_OUOTES = '"'; var SINGE_OUOTES = "'"; var SPACE = " "; var SPECIAL_CHARACTERS = '\\\'"[]., '; var EXT_SPECIAL_CHARACTERS = '\\{}\'"[]., :'; var Tokenizer = module.exports = function(string, ext) { this._string = string; this._idx = -1; this._extended = ext; this.parseString = ''; }; Tokenizer.prototype = { /** * grabs the next token either from the peek operation or generates the * next token. */ next: function() { var nextToken = this._nextToken ? this._nextToken : getNext(this._string, this._idx, this._extended); this._idx = nextToken.idx; this._nextToken = false; this.parseString += nextToken.token.token; return nextToken.token; }, /** * will peak but not increment the tokenizer */ peek: function() { var nextToken = this._nextToken ? this._nextToken : getNext(this._string, this._idx, this._extended); this._nextToken = nextToken; return nextToken.token; } }; Tokenizer.toNumber = function toNumber(x) { if (!isNaN(+x)) { return +x; } return NaN; }; function toOutput(token, type, done) { return { token: token, done: done, type: type }; } function getNext(string, idx, ext) { var output = false; var token = ''; var specialChars = ext ? EXT_SPECIAL_CHARACTERS : SPECIAL_CHARACTERS; var done; do { done = idx + 1 >= string.length; if (done) { break; } // we have to peek at the next token var character = string[idx + 1]; if (character !== undefined && specialChars.indexOf(character) === -1) { token += character; ++idx; continue; } // The token to delimiting character transition. else if (token.length) { break; } ++idx; var type; switch (character) { case DOT_SEPARATOR: type = TokenTypes.dotSeparator; break; case COMMA_SEPARATOR: type = TokenTypes.commaSeparator; break; case OPENING_BRACKET: type = TokenTypes.openingBracket; break; case CLOSING_BRACKET: type = TokenTypes.closingBracket; break; case OPENING_BRACE: type = TokenTypes.openingBrace; break; case CLOSING_BRACE: type = TokenTypes.closingBrace; break; case SPACE: type = TokenTypes.space; break; case DOUBLE_OUOTES: case SINGE_OUOTES: type = TokenTypes.quote; break; case ESCAPE: type = TokenTypes.escape; break; case COLON: type = TokenTypes.colon; break; default: type = TokenTypes.unknown; break; } output = toOutput(character, type, false); break; } while (!done); if (!output && token.length) { output = toOutput(token, TokenTypes.token, false); } if (!output) { output = {done: true}; } return { token: output, idx: idx }; } },{"122":122}],131:[function(require,module,exports){ var toPaths = require(147); var toTree = require(148); module.exports = function collapse(paths) { var collapseMap = paths. reduce(function(acc, path) { var len = path.length; if (!acc[len]) { acc[len] = []; } acc[len].push(path); return acc; }, {}); Object. keys(collapseMap). forEach(function(collapseKey) { collapseMap[collapseKey] = toTree(collapseMap[collapseKey]); }); return toPaths(collapseMap); }; },{"147":147,"148":148}],132:[function(require,module,exports){ /*eslint-disable*/ module.exports = { innerReferences: 'References with inner references are not allowed.', circularReference: 'There appears to be a circular reference, maximum reference following exceeded.' }; },{}],133:[function(require,module,exports){ /** * Escapes a string by prefixing it with "_". This function should be used on * untrusted input before it is embedded into paths. The goal is to ensure that * no reserved words (ex. "$type") make their way into paths and consequently * JSON Graph objects. */ module.exports = function escape(str) { return "_" + str; }; },{}],134:[function(require,module,exports){ var errors = require(132); /** * performs the simplified cache reference follow. This * differs from get as there is just following and reporting, * not much else. * * @param {Object} cacheRoot * @param {Array} ref */ function followReference(cacheRoot, ref, maxRefFollow) { if (typeof maxRefFollow === "undefined") { maxRefFollow = 5; } var branch = cacheRoot; var node = branch; var refPath = ref; var depth = -1; var referenceCount = 0; while (++depth < refPath.length) { var key = refPath[depth]; node = branch[key]; if ( node === null || typeof node !== "object" || (node.$type && node.$type !== "ref") ) { break; } if (node.$type === "ref") { // Show stopper exception. This route is malformed. if (depth + 1 < refPath.length) { return { error: new Error(errors.innerReferences) }; } if (referenceCount >= maxRefFollow) { return { error: new Error(errors.circularReference) }; } refPath = node.value; depth = -1; branch = cacheRoot; referenceCount++; } else { branch = node; } } return { node: node, refPath: refPath }; } module.exports = followReference; },{"132":132}],135:[function(require,module,exports){ var iterateKeySet = require(138); /** * Tests to see if the intersection should be stripped from the * total paths. The only way this happens currently is if the entirety * of the path is contained in the tree. * @private */ module.exports = function hasIntersection(tree, path, depth) { var current = tree; var intersects = true; // Continue iteratively going down a path until a complex key is // encountered, then recurse. for (;intersects && depth < path.length; ++depth) { var key = path[depth]; var keyType = typeof key; // We have to iterate key set if (key && keyType === 'object') { var note = {}; var innerKey = iterateKeySet(key, note); var nextDepth = depth + 1; // Loop through the innerKeys setting the intersects flag // to each result. Break out on false. do { var next = current[innerKey]; intersects = next !== undefined; if (intersects) { intersects = hasIntersection(next, path, nextDepth); } innerKey = iterateKeySet(key, note); } while (intersects && !note.done); // Since we recursed, we shall not pass any further! break; } // Its a simple key, just move forward with the testing. current = current[key]; intersects = current !== undefined; } return intersects; }; },{"138":138}],136:[function(require,module,exports){ // @flow /*:: import type { Key, KeySet, PathSet, Path, JsonGraph, JsonGraphNode, JsonMap } from "falcor-json-graph"; export type PathTree = { [key: string]: PathTree | null | void }; export type LengthTree = { [key: number]: PathTree | void }; export type IteratorNote = { done?: boolean }; type FalcorPathUtils = { iterateKeySet(keySet: KeySet, note: IteratorNote): Key; toTree(paths: PathSet[]): PathTree; pathsComplementFromTree(paths: PathSet[], tree: PathTree): PathSet[]; pathsComplementFromLengthTree(paths: PathSet[], tree: LengthTree): PathSet[]; toJsonKey(obj: JsonMap): string; isJsonKey(key: Key): boolean; maybeJsonKey(key: Key): JsonMap | void; hasIntersection(tree: PathTree, path: PathSet, depth: number): boolean; toPaths(lengths: LengthTree): PathSet[]; isIntegerKey(key: Key): boolean; maybeIntegerKey(key: Key): number | void; collapse(paths: PathSet[]): PathSet[]; followReference( cacheRoot: JsonGraph, ref: Path, maxRefFollow?: number ): { error: Error } | { error?: empty, node: ?JsonGraphNode, refPath: Path }; optimizePathSets( cache: JsonGraph, paths: PathSet[], maxRefFollow?: number ): { error: Error } | { error?: empty, paths: PathSet[] }; pathCount(path: PathSet): number; escape(key: string): string; unescape(key: string): string; materialize(pathSet: PathSet, value: JsonGraphNode): JsonGraphNode; }; */ module.exports = ({ iterateKeySet: require(138), toTree: require(148), pathsComplementFromTree: require(144), pathsComplementFromLengthTree: require(143), toJsonKey: require(139).toJsonKey, isJsonKey: require(139).isJsonKey, maybeJsonKey: require(139).maybeJsonKey, hasIntersection: require(135), toPaths: require(147), isIntegerKey: require(137).isIntegerKey, maybeIntegerKey: require(137).maybeIntegerKey, collapse: require(131), followReference: require(134), optimizePathSets: require(141), pathCount: require(142), escape: require(133), unescape: require(149), materialize: require(140) }/*: FalcorPathUtils*/); },{"131":131,"133":133,"134":134,"135":135,"137":137,"138":138,"139":139,"140":140,"141":141,"142":142,"143":143,"144":144,"147":147,"148":148,"149":149}],137:[function(require,module,exports){ "use strict"; var MAX_SAFE_INTEGER = 9007199254740991; // Number.MAX_SAFE_INTEGER in es6 var abs = Math.abs; var isSafeInteger = Number.isSafeInteger || function isSafeInteger(num) { return typeof num === "number" && num % 1 === 0 && abs(num) <= MAX_SAFE_INTEGER; } /** * Return number if argument is a number or can be cast to a number which * roundtrips to the same string, otherwise return undefined. */ function maybeIntegerKey(val) { if (typeof val === "string") { var num = Number(val); if(isSafeInteger(num) && String(num) === val) { return num; } } else if (isSafeInteger(val)) { return val; } } /** * Return true if argument is a number or can be cast to a number which * roundtrips to the same string. */ function isIntegerKey(val) { if (typeof val === "string") { var num = Number(val); return isSafeInteger(num) && String(num) === val; } return isSafeInteger(val); } module.exports.isIntegerKey = isIntegerKey; module.exports.maybeIntegerKey = maybeIntegerKey; },{}],138:[function(require,module,exports){ var isArray = Array.isArray; /** * Takes in a keySet and a note attempts to iterate over it. * If the value is a primitive, the key will be returned and the note will * be marked done * If the value is an object, then each value of the range will be returned * and when finished the note will be marked done. * If the value is an array, each value will be iterated over, if any of the * inner values are ranges, those will be iterated over. When fully done, * the note will be marked done. * * @param {Object|Array|String|Number} keySet - * @param {Object} note - The non filled note * @returns {String|Number|undefined} - The current iteration value. * If undefined, then the keySet is empty * @public */ module.exports = function iterateKeySet(keySet, note) { if (note.isArray === undefined) { /*#__NOINLINE__*/ initializeNote(keySet, note); } // Array iteration if (note.isArray) { var nextValue; // Cycle through the array and pluck out the next value. do { if (note.loaded && note.rangeOffset > note.to) { ++note.arrayOffset; note.loaded = false; } var idx = note.arrayOffset, length = keySet.length; if (idx >= length) { note.done = true; break; } var el = keySet[note.arrayOffset]; // Inner range iteration. if (el !== null && typeof el === 'object') { if (!note.loaded) { initializeRange(el, note); } // Empty to/from if (note.empty) { continue; } nextValue = note.rangeOffset++; } // Primitive iteration in array. else { ++note.arrayOffset; nextValue = el; } } while (nextValue === undefined); return nextValue; } // Range iteration else if (note.isObject) { if (!note.loaded) { initializeRange(keySet, note); } if (note.rangeOffset > note.to) { note.done = true; return undefined; } return note.rangeOffset++; } // Primitive value else { if (!note.loaded) { note.loaded = true; return keySet; } note.done = true; return undefined; } }; function initializeRange(key, memo) { var from = memo.from = key.from || 0; var to = memo.to = key.to || (typeof key.length === 'number' && memo.from + key.length - 1 || 0); memo.rangeOffset = memo.from; memo.loaded = true; if (from > to) { memo.empty = true; } } function initializeNote(key, note) { note.done = false; var isObject = note.isObject = !!(key && typeof key === 'object'); note.isArray = isObject && isArray(key); note.arrayOffset = 0; } },{}],139:[function(require,module,exports){ "use strict"; /** * Helper for getting a reproducible, key-sorted string representation of object. * Used to interpret an object as a falcor key. * @function * @param {Object} obj * @return stringified object with sorted keys. */ function toJsonKey(obj) { if (Object.prototype.toString.call(obj) === "[object Object]") { var key = JSON.stringify(obj, replacer); if (key[0] === "{") { return key; } } throw new TypeError("Only plain objects can be converted to JSON keys") } function replacer(key, value) { if (typeof value !== "object" || value === null || Array.isArray(value)) { return value; } return Object.keys(value) .sort() .reduce(function (acc, k) { acc[k] = value[k]; return acc; }, {}); } function maybeJsonKey(key) { if (typeof key !== 'string' || key[0] !== '{') { return; } var parsed; try { parsed = JSON.parse(key); } catch (e) { return; } if (JSON.stringify(parsed, replacer) !== key) { return; } return parsed; } function isJsonKey(key) { return typeof maybeJsonKey(key) !== "undefined"; } module.exports.toJsonKey = toJsonKey; module.exports.isJsonKey = isJsonKey; module.exports.maybeJsonKey = maybeJsonKey; },{}],140:[function(require,module,exports){ 'use strict'; var iterateKeySet = require(138); /** * Construct a jsonGraph from a pathSet and a value. * * @param {PathSet} pathSet - pathSet of paths at which to materialize value. * @param {JsonGraphNode} value - value to materialize at pathSet paths. * @returns {JsonGraphNode} - JsonGraph of value at pathSet paths. * @public */ module.exports = function materialize(pathSet, value) { return pathSet.reduceRight(function materializeInner(acc, keySet) { var branch = {}; if (typeof keySet !== 'object' || keySet === null) { branch[keySet] = acc; return branch; } var iteratorNote = {}; var key = iterateKeySet(keySet, iteratorNote); while (!iteratorNote.done) { branch[key] = acc; key = iterateKeySet(keySet, iteratorNote); } return branch; }, value); }; },{"138":138}],141:[function(require,module,exports){ var iterateKeySet = require(138); var cloneArray = require(146); var catAndSlice = require(145); var followReference = require(134); /** * The fastest possible optimize of paths. * * What it does: * - Any atom short-circuit / found value will be removed from the path. * - All paths will be exploded which means that collapse will need to be * ran afterwords. * - Any missing path will be optimized as much as possible. */ module.exports = function optimizePathSets(cache, paths, maxRefFollow) { if (typeof maxRefFollow === "undefined") { maxRefFollow = 5; } var optimized = []; for (var i = 0, len = paths.length; i < len; ++i) { var error = optimizePathSet(cache, cache, paths[i], 0, optimized, [], maxRefFollow); if (error) { return { error: error }; } } return { paths: optimized }; }; /** * optimizes one pathSet at a time. */ function optimizePathSet(cache, cacheRoot, pathSet, depth, out, optimizedPath, maxRefFollow) { // at missing, report optimized path. if (cache === undefined) { out[out.length] = catAndSlice(optimizedPath, pathSet, depth); return; } // all other sentinels are short circuited. // Or we found a primitive (which includes null) if (cache === null || (cache.$type && cache.$type !== "ref") || (typeof cache !== 'object')) { return; } // If the reference is the last item in the path then do not // continue to search it. if (cache.$type === "ref" && depth === pathSet.length) { return; } var keySet = pathSet[depth]; var isKeySet = typeof keySet === 'object' && keySet !== null; var nextDepth = depth + 1; var iteratorNote = false; var key = keySet; if (isKeySet) { iteratorNote = {}; key = iterateKeySet(keySet, iteratorNote); } var next, nextOptimized; do { next = cache[key]; var optimizedPathLength = optimizedPath.length; optimizedPath[optimizedPathLength] = key; if (next && next.$type === "ref" && nextDepth < pathSet.length) { var refResults = followReference(cacheRoot, next.value, maxRefFollow); if (refResults.error) { return refResults.error; } next = refResults.node; // must clone to avoid the mutation from above destroying the cache. nextOptimized = cloneArray(refResults.refPath); } else { nextOptimized = optimizedPath; } var error = optimizePathSet(next, cacheRoot, pathSet, nextDepth, out, nextOptimized, maxRefFollow); if (error) { return error; } optimizedPath.length = optimizedPathLength; if (iteratorNote && !iteratorNote.done) { key = iterateKeySet(keySet, iteratorNote); } } while (iteratorNote && !iteratorNote.done); } },{"134":134,"138":138,"145":145,"146":146}],142:[function(require,module,exports){ "use strict"; /** * Helper for getPathCount. Used to determine the size of a key or range. * @function * @param {Object} rangeOrKey * @return The size of the key or range passed in. */ function getRangeOrKeySize(rangeOrKey) { if (rangeOrKey == null) { return 1; } else if (Array.isArray(rangeOrKey)) { throw new Error("Unexpected Array found in keySet: " + JSON.stringify(rangeOrKey)); } else if (typeof rangeOrKey === "object") { return getRangeSize(rangeOrKey); } else { return 1; } } /** * Returns the size (number of items) in a Range, * @function * @param {Object} range The Range with both "from" and "to", or just "to" * @return The number of items in the range. */ function getRangeSize(range) { var to = range.to; var length = range.length; if (to != null) { if (isNaN(to) || parseInt(to, 10) !== to) { throw new Error("Invalid range, 'to' is not an integer: " + JSON.stringify(range)); } var from = range.from || 0; if (isNaN(from) || parseInt(from, 10) !== from) { throw new Error("Invalid range, 'from' is not an integer: " + JSON.stringify(range)); } if (from <= to) { return (to - from) + 1; } else { return 0; } } else if (length != null) { if (isNaN(length) || parseInt(length, 10) !== length) { throw new Error("Invalid range, 'length' is not an integer: " + JSON.stringify(range)); } else { return length; } } else { throw new Error("Invalid range, expected 'to' or 'length': " + JSON.stringify(range)); } } /** * Returns a count of the number of paths this pathset * represents. * * For example, ["foo", {"from":0, "to":10}, "bar"], * would represent 11 paths (0 to 10, inclusive), and * ["foo, ["baz", "boo"], "bar"] would represent 2 paths. * * @function * @param {Object[]} pathSet the path set. * * @return The number of paths this represents */ function getPathCount(pathSet) { if (pathSet.length === 0) { throw new Error("All paths must have length larger than zero."); } var numPaths = 1; for (var i = 0; i < pathSet.length; i++) { var segment = pathSet[i]; if (Array.isArray(segment)) { var numKeys = 0; for (var j = 0; j < segment.length; j++) { var keySet = segment[j]; numKeys += getRangeOrKeySize(keySet); } numPaths *= numKeys; } else { numPaths *= getRangeOrKeySize(segment); } } return numPaths; } module.exports = getPathCount; },{}],143:[function(require,module,exports){ var hasIntersection = require(135); /** * Compares the paths passed in with the tree. Any of the paths that are in * the tree will be stripped from the paths. * * **Does not mutate** the incoming paths object. * **Proper subset** only matching. * * @param {Array} paths - A list of paths (complex or simple) to strip the * intersection * @param {Object} tree - * @public */ module.exports = function pathsComplementFromLengthTree(paths, tree) { var out = []; var outLength = -1; for (var i = 0, len = paths.length; i < len; ++i) { // If this does not intersect then add it to the output. var path = paths[i]; if (!hasIntersection(tree[path.length], path, 0)) { out[++outLength] = path; } } return out; }; },{"135":135}],144:[function(require,module,exports){ var hasIntersection = require(135); /** * Compares the paths passed in with the tree. Any of the paths that are in * the tree will be stripped from the paths. * * **Does not mutate** the incoming paths object. * **Proper subset** only matching. * * @param {Array} paths - A list of paths (complex or simple) to strip the * intersection * @param {Object} tree - * @public */ module.exports = function pathsComplementFromTree(paths, tree) { var out = []; var outLength = -1; for (var i = 0, len = paths.length; i < len; ++i) { // If this does not intersect then add it to the output. if (!hasIntersection(tree, paths[i], 0)) { out[++outLength] = paths[i]; } } return out; }; },{"135":135}],145:[function(require,module,exports){ module.exports = function catAndSlice(a, b, slice) { var next = [], i, j, len; for (i = 0, len = a.length; i < len; ++i) { next[i] = a[i]; } for (j = slice || 0, len = b.length; j < len; ++j, ++i) { next[i] = b[j]; } return next; }; },{}],146:[function(require,module,exports){ function cloneArray(arr, index) { var a = []; var len = arr.length; for (var i = index || 0; i < len; i++) { a[i] = arr[i]; } return a; } module.exports = cloneArray; },{}],147:[function(require,module,exports){ var maybeIntegerKey = require(137).maybeIntegerKey; var isIntegerKey = require(137).isIntegerKey; var isArray = Array.isArray; var typeOfObject = "object"; var typeOfNumber = "number"; /* jshint forin: false */ module.exports = function toPaths(lengths) { var pathmap; var allPaths = []; for (var length in lengths) { var num = maybeIntegerKey(length); if (typeof num === typeOfNumber && isObject(pathmap = lengths[length])) { var paths = collapsePathMap(pathmap, 0, num).sets; var pathsIndex = -1; var pathsCount = paths.length; while (++pathsIndex < pathsCount) { allPaths.push(collapsePathSetIndexes(paths[pathsIndex])); } } } return allPaths; }; function isObject(value) { return value !== null && typeof value === typeOfObject; } function collapsePathMap(pathmap, depth, length) { var key; var code = getHashCode(String(depth)); var subs = Object.create(null); var codes = []; var codesIndex = -1; var codesCount = 0; var pathsets = []; var pathsetsCount = 0; var subPath, subCode, subKeys, subKeysIndex, subKeysCount, subSets, subSetsIndex, subSetsCount, pathset, pathsetIndex, pathsetCount, firstSubKey, pathsetClone; subKeys = []; subKeysIndex = -1; if (depth < length - 1) { subKeysCount = getKeys(pathmap, subKeys); while (++subKeysIndex < subKeysCount) { key = subKeys[subKeysIndex]; subPath = collapsePathMap(pathmap[key], depth + 1, length); subCode = subPath.code; if(subs[subCode]) { subPath = subs[subCode]; } else { codes[codesCount++] = subCode; subPath = subs[subCode] = { keys: [], sets: subPath.sets }; } code = getHashCode(code + key + subCode); var num = maybeIntegerKey(key); subPath.keys.push(typeof num === typeOfNumber ? num : key); } while(++codesIndex < codesCount) { key = codes[codesIndex]; subPath = subs[key]; subKeys = subPath.keys; subKeysCount = subKeys.length; if (subKeysCount > 0) { subSets = subPath.sets; subSetsIndex = -1; subSetsCount = subSets.length; firstSubKey = subKeys[0]; while (++subSetsIndex < subSetsCount) { pathset = subSets[subSetsIndex]; pathsetIndex = -1; pathsetCount = pathset.length; pathsetClone = new Array(pathsetCount + 1); pathsetClone[0] = subKeysCount > 1 && subKeys || firstSubKey; while (++pathsetIndex < pathsetCount) { pathsetClone[pathsetIndex + 1] = pathset[pathsetIndex]; } pathsets[pathsetsCount++] = pathsetClone; } } } } else { subKeysCount = getKeys(pathmap, subKeys); if (subKeysCount > 1) { pathsets[pathsetsCount++] = [subKeys]; } else { pathsets[pathsetsCount++] = subKeys; } while (++subKeysIndex < subKeysCount) { code = getHashCode(code + subKeys[subKeysIndex]); } } return { code: code, sets: pathsets }; } function collapsePathSetIndexes(pathset) { var keysetIndex = -1; var keysetCount = pathset.length; while (++keysetIndex < keysetCount) { var keyset = pathset[keysetIndex]; if (isArray(keyset)) { pathset[keysetIndex] = collapseIndex(keyset); } } return pathset; } /** * Collapse range indexers, e.g. when there is a continuous * range in an array, turn it into an object instead: * * [1,2,3,4,5,6] => {"from":1, "to":6} * * @private */ function collapseIndex(keyset) { // Do we need to dedupe an indexer keyset if they're duplicate consecutive integers? // var hash = {}; var keyIndex = -1; var keyCount = keyset.length - 1; var isSparseRange = keyCount > 0; while (++keyIndex <= keyCount) { var key = keyset[keyIndex]; if (!isIntegerKey(key) /* || hash[key] === true*/ ) { isSparseRange = false; break; } // hash[key] = true; // Cast number indexes to integers. keyset[keyIndex] = parseInt(key, 10); } if (isSparseRange === true) { keyset.sort(sortListAscending); var from = keyset[0]; var to = keyset[keyCount]; // If we re-introduce deduped integer indexers, change this comparson to "===". if (to - from <= keyCount) { return { from: from, to: to }; } } return keyset; } function sortListAscending(a, b) { return a - b; } /* jshint forin: false */ function getKeys(map, keys, sort) { var len = 0; for (var key in map) { keys[len++] = key; } return len; } function getHashCode(key) { var code = 5381; var index = -1; var count = key.length; while (++index < count) { code = (code << 5) + code + key.charCodeAt(index); } return String(code); } // backwards-compatibility (temporary) module.exports._isSafeNumber = isIntegerKey; },{"137":137}],148:[function(require,module,exports){ var iterateKeySet = require(138); /** * @param {Array} paths - * @returns {Object} - */ module.exports = function toTree(paths) { return paths.reduce(__reducer, {}); }; function __reducer(acc, path) { /*#__NOINLINE__*/ innerToTree(acc, path, 0); return acc; } function innerToTree(seed, path, depth) { var keySet = path[depth]; var iteratorNote = {}; var key; var nextDepth = depth + 1; key = iterateKeySet(keySet, iteratorNote); while (!iteratorNote.done) { var next = Object.prototype.hasOwnProperty.call(seed, key) && seed[key]; if (!next) { if (nextDepth === path.length) { seed[key] = null; } else if (key !== undefined) { next = seed[key] = {}; } } if (nextDepth < path.length) { innerToTree(next, path, nextDepth); } key = iterateKeySet(keySet, iteratorNote); } } },{"138":138}],149:[function(require,module,exports){ /** * Unescapes a string by removing the leading "_". This function is the inverse * of escape, which is used to encode untrusted input to ensure it * does not contain reserved JSON Graph keywords (ex. "$type"). */ module.exports = function unescape(str) { if (str.slice(0, 1) === "_") { return str.slice(1); } else { throw SyntaxError("Expected \"_\"."); } }; },{}],150:[function(require,module,exports){ 'use strict'; module.exports = require(155) },{"155":155}],151:[function(require,module,exports){ (function (Promise){(function (){ 'use strict'; var asap = require(114); function noop() {} // States: // // 0 - pending // 1 - fulfilled with _value // 2 - rejected with _value // 3 - adopted the state of another promise, _value // // once the state is no longer pending (0) it is immutable // All `_` prefixed properties will be reduced to `_{random number}` // at build time to obfuscate them and discourage their use. // We don't use symbols or Object.defineProperty to fully hide them // because the performance isn't good enough. // to avoid using try/catch inside critical functions, we // extract them to here. var LAST_ERROR = null; var IS_ERROR = {}; function getThen(obj) { try { return obj.then; } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } function tryCallOne(fn, a) { try { return fn(a); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } function tryCallTwo(fn, a, b) { try { fn(a, b); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } } module.exports = Promise; function Promise(fn) { if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } if (typeof fn !== 'function') { throw new TypeError('Promise constructor\'s argument is not a function'); } this._U = 0; this._V = 0; this._W = null; this._X = null; if (fn === noop) return; doResolve(fn, this); } Promise._Y = null; Promise._Z = null; Promise._0 = noop; Promise.prototype.then = function(onFulfilled, onRejected) { if (this.constructor !== Promise) { return safeThen(this, onFulfilled, onRejected); } var res = new Promise(noop); handle(this, new Handler(onFulfilled, onRejected, res)); return res; }; function safeThen(self, onFulfilled, onRejected) { return new self.constructor(function (resolve, reject) { var res = new Promise(noop); res.then(resolve, reject); handle(self, new Handler(onFulfilled, onRejected, res)); }); } function handle(self, deferred) { while (self._V === 3) { self = self._W; } if (Promise._Y) { Promise._Y(self); } if (self._V === 0) { if (self._U === 0) { self._U = 1; self._X = deferred; return; } if (self._U === 1) { self._U = 2; self._X = [self._X, deferred]; return; } self._X.push(deferred); return; } handleResolved(self, deferred); } function handleResolved(self, deferred) { asap(function() { var cb = self._V === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { if (self._V === 1) { resolve(deferred.promise, self._W); } else { reject(deferred.promise, self._W); } return; } var ret = tryCallOne(cb, self._W); if (ret === IS_ERROR) { reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); } function resolve(self, newValue) { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) { return reject( self, new TypeError('A promise cannot be resolved with itself.') ); } if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { var then = getThen(newValue); if (then === IS_ERROR) { return reject(self, LAST_ERROR); } if ( then === self.then && newValue instanceof Promise ) { self._V = 3; self._W = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(then.bind(newValue), self); return; } } self._V = 1; self._W = newValue; finale(self); } function reject(self, newValue) { self._V = 2; self._W = newValue; if (Promise._Z) { Promise._Z(self, newValue); } finale(self); } function finale(self) { if (self._U === 1) { handle(self, self._X); self._X = null; } if (self._U === 2) { for (var i = 0; i < self._X.length; i++) { handle(self, self._X[i]); } self._X = null; } } function Handler(onFulfilled, onRejected, promise){ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, promise) { var done = false; var res = tryCallTwo(fn, function (value) { if (done) return; done = true; resolve(promise, value); }, function (reason) { if (done) return; done = true; reject(promise, reason); }); if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } } }).call(this)}).call(this,typeof Promise === "function" ? Promise : require(150)) },{"114":114,"150":150}],152:[function(require,module,exports){ 'use strict'; var Promise = require(151); module.exports = Promise; Promise.prototype.done = function (onFulfilled, onRejected) { var self = arguments.length ? this.then.apply(this, arguments) : this; self.then(null, function (err) { setTimeout(function () { throw err; }, 0); }); }; },{"151":151}],153:[function(require,module,exports){ 'use strict'; //This file contains the ES6 extensions to the core Promises/A+ API var Promise = require(151); module.exports = Promise; /* Static Functions */ var TRUE = valuePromise(true); var FALSE = valuePromise(false); var NULL = valuePromise(null); var UNDEFINED = valuePromise(undefined); var ZERO = valuePromise(0); var EMPTYSTRING = valuePromise(''); function valuePromise(value) { var p = new Promise(Promise._0); p._V = 1; p._W = value; return p; } Promise.resolve = function (value) { if (value instanceof Promise) return value; if (value === null) return NULL; if (value === undefined) return UNDEFINED; if (value === true) return TRUE; if (value === false) return FALSE; if (value === 0) return ZERO; if (value === '') return EMPTYSTRING; if (typeof value === 'object' || typeof value === 'function') { try { var then = value.then; if (typeof then === 'function') { return new Promise(then.bind(value)); } } catch (ex) { return new Promise(function (resolve, reject) { reject(ex); }); } } return valuePromise(value); }; var iterableToArray = function (iterable) { if (typeof Array.from === 'function') { // ES2015+, iterables exist iterableToArray = Array.from; return Array.from(iterable); } // ES5, only arrays and array-likes exist iterableToArray = function (x) { return Array.prototype.slice.call(x); }; return Array.prototype.slice.call(iterable); } Promise.all = function (arr) { var args = iterableToArray(arr); return new Promise(function (resolve, reject) { if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { if (val instanceof Promise && val.then === Promise.prototype.then) { while (val._V === 3) { val = val._W; } if (val._V === 1) return res(i, val._W); if (val._V === 2) reject(val._W); val.then(function (val) { res(i, val); }, reject); return; } else { var then = val.then; if (typeof then === 'function') { var p = new Promise(then.bind(val)); p.then(function (val) { res(i, val); }, reject); return; } } } args[i] = val; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; Promise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); }; Promise.race = function (values) { return new Promise(function (resolve, reject) { iterableToArray(values).forEach(function(value){ Promise.resolve(value).then(resolve, reject); }); }); }; /* Prototype Methods */ Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); }; },{"151":151}],154:[function(require,module,exports){ 'use strict'; var Promise = require(151); module.exports = Promise; Promise.prototype.finally = function (f) { return this.then(function (value) { return Promise.resolve(f()).then(function () { return value; }); }, function (err) { return Promise.resolve(f()).then(function () { throw err; }); }); }; },{"151":151}],155:[function(require,module,exports){ 'use strict'; module.exports = require(151); require(152); require(154); require(153); require(156); require(157); },{"151":151,"152":152,"153":153,"154":154,"156":156,"157":157}],156:[function(require,module,exports){ 'use strict'; // This file contains then/promise specific extensions that are only useful // for node.js interop var Promise = require(151); var asap = require(113); module.exports = Promise; /* Static Functions */ Promise.denodeify = function (fn, argumentCount) { if ( typeof argumentCount === 'number' && argumentCount !== Infinity ) { return denodeifyWithCount(fn, argumentCount); } else { return denodeifyWithoutCount(fn); } }; var callbackFn = ( 'function (err, res) {' + 'if (err) { rj(err); } else { rs(res); }' + '}' ); function denodeifyWithCount(fn, argumentCount) { var args = []; for (var i = 0; i < argumentCount; i++) { args.push('a' + i); } var body = [ 'return function (' + args.join(',') + ') {', 'var self = this;', 'return new Promise(function (rs, rj) {', 'var res = fn.call(', ['self'].concat(args).concat([callbackFn]).join(','), ');', 'if (res &&', '(typeof res === "object" || typeof res === "function") &&', 'typeof res.then === "function"', ') {rs(res);}', '});', '};' ].join(''); return Function(['Promise', 'fn'], body)(Promise, fn); } function denodeifyWithoutCount(fn) { var fnLength = Math.max(fn.length - 1, 3); var args = []; for (var i = 0; i < fnLength; i++) { args.push('a' + i); } var body = [ 'return function (' + args.join(',') + ') {', 'var self = this;', 'var args;', 'var argLength = arguments.length;', 'if (arguments.length > ' + fnLength + ') {', 'args = new Array(arguments.length + 1);', 'for (var i = 0; i < arguments.length; i++) {', 'args[i] = arguments[i];', '}', '}', 'return new Promise(function (rs, rj) {', 'var cb = ' + callbackFn + ';', 'var res;', 'switch (argLength) {', args.concat(['extra']).map(function (_, index) { return ( 'case ' + (index) + ':' + 'res = fn.call(' + ['self'].concat(args.slice(0, index)).concat('cb').join(',') + ');' + 'break;' ); }).join(''), 'default:', 'args[argLength] = cb;', 'res = fn.apply(self, args);', '}', 'if (res &&', '(typeof res === "object" || typeof res === "function") &&', 'typeof res.then === "function"', ') {rs(res);}', '});', '};' ].join(''); return Function( ['Promise', 'fn'], body )(Promise, fn); } Promise.nodeify = function (fn) { return function () { var args = Array.prototype.slice.call(arguments); var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null; var ctx = this; try { return fn.apply(this, arguments).nodeify(callback, ctx); } catch (ex) { if (callback === null || typeof callback == 'undefined') { return new Promise(function (resolve, reject) { reject(ex); }); } else { asap(function () { callback.call(ctx, ex); }) } } } }; Promise.prototype.nodeify = function (callback, ctx) { if (typeof callback != 'function') return this; this.then(function (value) { asap(function () { callback.call(ctx, null, value); }); }, function (err) { asap(function () { callback.call(ctx, err); }); }); }; },{"113":113,"151":151}],157:[function(require,module,exports){ 'use strict'; var Promise = require(151); module.exports = Promise; Promise.enableSynchronous = function () { Promise.prototype.isPending = function() { return this.getState() == 0; }; Promise.prototype.isFulfilled = function() { return this.getState() == 1; }; Promise.prototype.isRejected = function() { return this.getState() == 2; }; Promise.prototype.getValue = function () { if (this._V === 3) { return this._W.getValue(); } if (!this.isFulfilled()) { throw new Error('Cannot get a value of an unfulfilled promise.'); } return this._W; }; Promise.prototype.getReason = function () { if (this._V === 3) { return this._W.getReason(); } if (!this.isRejected()) { throw new Error('Cannot get a rejection reason of a non-rejected promise.'); } return this._W; }; Promise.prototype.getState = function () { if (this._V === 3) { return this._W.getState(); } if (this._V === -1 || this._V === -2) { return 0; } return this._V; }; }; Promise.disableSynchronous = function() { Promise.prototype.isPending = undefined; Promise.prototype.isFulfilled = undefined; Promise.prototype.isRejected = undefined; Promise.prototype.getValue = undefined; Promise.prototype.getReason = undefined; Promise.prototype.getState = undefined; }; },{"151":151}],158:[function(require,module,exports){ (function (global){(function (){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _ponyfill = require(159); var _ponyfill2 = _interopRequireDefault(_ponyfill); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var root; /* global window */ if (typeof self !== 'undefined') { root = self; } else if (typeof window !== 'undefined') { root = window; } else if (typeof global !== 'undefined') { root = global; } else if (typeof module !== 'undefined') { root = module; } else { root = Function('return this')(); } var result = (0, _ponyfill2['default'])(root); exports['default'] = result; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"159":159}],159:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports['default'] = symbolObservablePonyfill; function symbolObservablePonyfill(root) { var result; var _Symbol = root.Symbol; if (typeof _Symbol === 'function') { if (_Symbol.observable) { result = _Symbol.observable; } else { result = _Symbol('observable'); _Symbol.observable = result; } } else { result = '@@observable'; } return result; }; },{}]},{},[1])(1) }); ================================================ FILE: doc/DataSource.html ================================================ --- layout: api-page title: "Class: DataSource" id: api ---

    DataSource

    (abstract) new DataSource()

    A DataSource is an interface which can be implemented to expose JSON Graph information to a Model. Every DataSource is associated with a single JSON Graph object. Models execute JSON Graph operations (get, set, and call) to retrieve values from the DataSource’s JSON Graph object. DataSources may retrieve JSON Graph information from anywhere, including device memory, a remote machine, or even a lazily-run computation.

    Source:
    typedefs/DataSource.js, line 1

    Methods

    call(functionPath, args, refSuffixes, extraPaths) → {Observable.<JSONGraphEnvelope>}

    Invokes a function in the DataSource's JSONGraph object.

    Parameters:
    Name Type Description
    functionPath Path

    the path to the function to invoke

    args Array.<Object>

    the arguments to pass to the function

    refSuffixes Array.<PathSet>

    paths to retrieve from the targets of JSONGraph References in the function's response.

    extraPaths Array.<PathSet>

    additional paths to retrieve after successful function execution

    Source:
    typedefs/DataSource.js, line 25
    Returns:

    jsonGraphEnvelope the response returned from the server.

    Return Type:
    Observable.<JSONGraphEnvelope>

    get(pathSets) → {Observable.<JSONGraphEnvelope>}

    The get method retrieves values from the DataSource's associated JSONGraph object.

    Parameters:
    Name Type Description
    pathSets Array.<PathSet>

    the path(s) to retrieve

    Source:
    typedefs/DataSource.js, line 7
    Returns:

    jsonGraphEnvelope the response returned from the server.

    Return Type:
    Observable.<JSONGraphEnvelope>

    set(jsonGraphEnvelope) → {Observable.<JSONGraphEnvelope>}

    The set method accepts values to set in the DataSource's associated JSONGraph object.

    Parameters:
    Name Type Description
    jsonGraphEnvelope JSONGraphEnvelope

    a JSONGraphEnvelope containing values to set in the DataSource's associated JSONGraph object.

    Source:
    typedefs/DataSource.js, line 16
    Returns:

    a JSONGraphEnvelope containing all of the requested values after the set operation.

    Return Type:
    Observable.<JSONGraphEnvelope>
    ================================================ FILE: doc/FromEsObserverAdapter.html ================================================ --- layout: api-page title: "Class: FromEsObserverAdapter" id: api ---

    FromEsObserverAdapter

    new FromEsObserverAdapter()

    FromEsObserverAdapter is an adpater from an ES Observer to an Rx 2 Observer

    Source:
    toEsObservable.js, line 1
    ================================================ FILE: doc/Model.html ================================================ --- layout: api-page title: "Class: Model" id: api ---

    Model

    new Model(ooptional)

    A Model object is used to execute commands against a JSONGraph object. Models can work with a local JSONGraph cache, or it can work with a remote JSONGraph object through a DataSource.

    Parameters:
    Name & Attributes Type Description
    o
    optional
    Options

    a set of options to customize behavior

    Source:
    Model.js, line 87

    Members

    getValue

    Get data for a single Path.

    Source:
    Model.js, line 365
    Example
    var model = new falcor.Model({source: new HttpDataSource("/model.json") });
    
     model.
         getValue('user.name').
         subscribe(function(name) {
             console.log(name);
         });
    
     // The code above prints "Jim" to the console.

    setValue

    Set value for a single Path.

    Source:
    Model.js, line 383
    Example
    var model = new falcor.Model({source: new HttpDataSource("/model.json") });
    
     model.
         setValue('user.name', 'Jim').
         subscribe(function(name) {
             console.log(name);
         });
    
     // The code above prints "Jim" to the console.

    Methods

    _setMaxSize(maxSize)

    Reset cache maxSize. When the new maxSize is smaller than the old force a collect.

    Parameters:
    Name Type Description
    maxSize Number

    the new maximum cache size

    Source:
    Model.js, line 466

    asDataSource() → {DataSource}

    Adapts a Model to the DataSource interface.

    Source:
    Model.js, line 596
    Return Type:
    DataSource
    Example
    var model =
        new falcor.Model({
            cache: {
                user: {
                    name: "Steve",
                    surname: "McGuire"
                }
            }
        }),
        proxyModel = new falcor.Model({ source: model.asDataSource() });
    
    // Prints "Steve"
    proxyModel.getValue("user.name").
        then(function(name) {
            console.log(name);
        });

    batch(schedulerOrDelay) → {Model}

    Returns a clone of the Model that enables batching. Within the configured time period, paths for get operations are collected and sent to the DataSource in a batch. Batching can be more efficient if the DataSource access the network, potentially reducing the number of HTTP requests to the server.

    Parameters:
    Name Type Description
    schedulerOrDelay Scheduler or number

    Either a Scheduler that determines when to send a batch to the DataSource, or the number in milliseconds to collect a batch before sending to the DataSource. If this parameter is omitted, then batch collection ends at the end of the next tick.

    Source:
    Model.js, line 536
    Returns:

    a Model which schedules a batch of get requests to the DataSource.

    Return Type:
    Model

    boxValues() → {Model}

    Returns a clone of the Model that boxes values returning the wrapper (Atom, Reference, or Error), rather than the value inside it. This allows any metadata attached to the wrapper to be inspected.

    Source:
    Model.js, line 616
    Return Type:
    Model

    call(functionPath, args, refPaths, extraPaths)

    Invokes a function in the JSON Graph.

    Parameters:
    Name Type Description
    functionPath Path

    the path to the function to invoke

    args Array.<Object>

    the arguments to pass to the function

    refPaths Array.<PathSet>

    the paths to retrieve from the JSON Graph References in the message returned from the function

    extraPaths Array.<PathSet>

    additional paths to retrieve after successful function execution

    Source:
    Model.js, line 242
    Returns:

    {ModelResponse. - a JSONEnvelope contains the values returned from the function

    deref(responseObject) → {Model}

    Returns a new Model bound to a location within the JSONGraph. The bound location is never a Reference: any References encountered while resolving the bound Path are always replaced with the References target value. For subsequent operations on the Model, all paths will be evaluated relative to the bound path. Deref allows you to:

    • Expose only a fragment of the JSONGraph to components, rather than the entire graph
    • Hide the location of a JSONGraph fragment from components
    • Optimize for executing multiple operations and path looksup at/below the same location in the JSONGraph
    Parameters:
    Name Type Description
    responseObject Object

    an object previously retrieved from the Model

    Source:
    Model.js, line 335
    Returns:
    Return Type:
    Model
    Example
    var Model = falcor.Model;
    var model = new Model({
      cache: {
        users: [
          Model.ref(["usersById", 32])
        ],
        usersById: {
          32: {
            name: "Steve",
            surname: "McGuire"
          }
        }
      }
    });
    
    model.
        get(['users', 0, 'name']).
        subscribe(function(jsonEnv) {
            var userModel = model.deref(jsonEnv.json.users[0]);
            console.log(model.getPath());
            console.log(userModel.getPath());
       });
    });
    
    // prints the following:
    // []
    // ["usersById", 32] - because userModel refers to target of reference at ["users", 0]

    get(…path) → {ModelResponse.<JSONEnvelope>}

    The get method retrieves several Paths or PathSets from a Model. The get method loads each value into a JSON object and returns in a ModelResponse.

    Parameters:
    Name & Attributes Type Description
    path
    repeatable
    PathSet

    the path(s) to retrieve

    Source:
    Model.js, line 175
    Returns:
    • the requested data as JSON
    Return Type:
    ModelResponse.<JSONEnvelope>

    getCache(…pathSetsoptional) → {JSONGraph}

    Get the local JSONGraph cache. This method can be a useful to store the state of the cache.

    Parameters:
    Name & Attributes Type Description
    pathSets
    optional
    repeatable
    Array.<PathSet>

    The path(s) to retrieve. If no paths are specified, the entire JSONGraph is returned.

    Source:
    Model.js, line 449
    Returns:

    all of the JSONGraph data in the Model cache.

    Return Type:
    JSONGraph
    Example
    // Storing the boxshot of the first 10 titles in the first 10 genreLists to local storage.
     localStorage.setItem('cache', JSON.stringify(model.getCache("genreLists[0...10][0...10].boxshot")));

    getPath() → {Path}

    Returns the Path to the object within the JSON Graph that this Model references.

    Source:
    Model.js, line 681
    Return Type:
    Path
    Example
    var Model = falcor.Model;
    var model = new Model({
      cache: {
        users: [
          Model.ref(["usersById", 32])
        ],
        usersById: {
          32: {
            name: "Steve",
            surname: "McGuire"
          }
        }
      }
    });
    
    model.
        get(['users', 0, 'name']).
        subscribe(function(jsonEnv) {
            var userModel = model.deref(jsonEnv.json.users[0]);
            console.log(model.getPath());
            console.log(userModel.getPath());
       });
    });
    
    // prints the following:
    // []
    // ["usersById", 32] - because userModel refers to target of reference at ["users", 0]

    getVersion(pathnullable) → {Number}

    Retrieves a number which is incremented every single time a value is changed underneath the Model or the object at an optionally-provided Path beneath the Model.

    Parameters:
    Name & Attributes Type Description
    path
    nullable
    Path

    a path at which to retrieve the version number

    Source:
    Model.js, line 490
    Returns:

    a version number which changes whenever a value is changed underneath the Model or provided Path

    Return Type:
    Number

    invalidate(…path)

    The invalidate method synchronously removes several Paths or PathSets from a Model cache.

    Parameters:
    Name & Attributes Type Description
    path
    repeatable
    PathSet

    the paths to remove from the Model's cache.

    Source:
    Model.js, line 272

    preload(…path) → {ModelResponse.<JSONEnvelope>}

    The preload method retrieves several Paths or PathSets from a Model and loads them into the Model cache.

    Parameters:
    Name & Attributes Type Description
    path
    repeatable
    PathSet

    the path(s) to retrieve

    Source:
    Model.js, line 211
    Returns:
    • a ModelResponse that completes when the data has been loaded into the cache.
    Return Type:
    ModelResponse.<JSONEnvelope>

    set() → {ModelResponse.<JSONEnvelope>}

    Sets the value at one or more places in the JSONGraph model. The set method accepts one or more PathValues, each of which is a combination of a location in the document and the value to place there. In addition to accepting PathValues, the set method also returns the values after the set operation is complete.

    Source:
    Model.js, line 203
    Returns:
    • an Observable stream containing the values in the JSONGraph model after the set was attempted
    Return Type:
    ModelResponse.<JSONEnvelope>

    setCache(jsonGraph)

    Set the local cache to a JSONGraph fragment. This method can be a useful way of mocking a remote document, or restoring the local cache from a previously stored state.

    Parameters:
    Name Type Description
    jsonGraph JSONGraph

    the JSONGraph fragment to use as the local cache

    Source:
    Model.js, line 411

    treatErrorsAsValues() → {Model}

    Returns a clone of the Model that treats errors as values. Errors will be reported in the same callback used to report data. Errors will appear as objects in responses, rather than being sent to the Observable~onErrorCallback callback of the ModelResponse.

    Source:
    Model.js, line 569
    Return Type:
    Model

    unbatch() → {Model}

    Returns a clone of the Model that disables batching. This is the default mode. Each get operation will be executed on the DataSource separately.

    Source:
    Model.js, line 552
    Returns:

    a Model that batches requests of the same type and sends them to the data source together

    Return Type:
    Model

    unboxValues() → {Model}

    Returns a clone of the Model that unboxes values, returning the value inside of the wrapper (Atom, Reference, or Error), rather than the wrapper itself. This is the default mode.

    Source:
    Model.js, line 626
    Return Type:
    Model

    withoutDataSource() → {Model}

    Returns a clone of the Model that only uses the local JSONGraph and never uses a DataSource to retrieve missing paths.

    Source:
    Model.js, line 636
    Return Type:
    Model

    Type Definitions

    comparator(existingValue, newValue) → {Boolean}

    This function is invoked every time a value in the Model cache is about to be replaced with a new value. If the function returns true, the existing value is replaced with a new value and the version flag on all of the value's ancestors in the tree are incremented.

    Parameters:
    Name Type Description
    existingValue Object

    the current value in the Model cache.

    newValue Object

    the value about to be set into the Model cache.

    Source:
    Model.js, line 51
    Returns:

    the Boolean value indicating whether the new value and the existing value are equal.

    Return Type:
    Boolean

    errorSelector(jsonGraphError) → {Object}

    This function is invoked on every JSONGraph Error retrieved from the DataSource. This function allows Error objects to be transformed before being stored in the Model's cache.

    Parameters:
    Name Type Description
    jsonGraphError Object

    the JSONGraph Error object to transform before it is stored in the Model's cache.

    Source:
    Model.js, line 43
    Returns:

    the JSONGraph Error object to store in the Model cache.

    Return Type:
    Object

    onChange()

    This callback is invoked when the Model's cache is changed.

    Source:
    Model.js, line 38
    ================================================ FILE: doc/Model.js.html ================================================ --- layout: api-page title: "Model.js" id: api ---

    Model.js

    var ModelRoot = require("./ModelRoot");
    var ModelDataSourceAdapter = require("./ModelDataSourceAdapter");
    
    var RequestQueue = require("./request/RequestQueueV2");
    var ModelResponse = require("./response/ModelResponse");
    var CallResponse = require("./response/CallResponse");
    var InvalidateResponse = require("./response/InvalidateResponse");
    
    var TimeoutScheduler = require("./schedulers/TimeoutScheduler");
    var ImmediateScheduler = require("./schedulers/ImmediateScheduler");
    
    var collectLru = require("./lru/collect");
    var pathSyntax = require("falcor-path-syntax");
    
    var getSize = require("./support/getSize");
    var isObject = require("./support/isObject");
    var isPrimitive = require("./support/isPrimitive");
    var isJSONEnvelope = require("./support/isJSONEnvelope");
    var isJSONGraphEnvelope = require("./support/isJSONGraphEnvelope");
    
    var setCache = require("./set/setPathMaps");
    var setJSONGraphs = require("./set/setJSONGraphs");
    var jsong = require("falcor-json-graph");
    var ID = 0;
    var validateInput = require("./support/validateInput");
    var noOp = function() {};
    var getCache = require("./get/getCache");
    var get = require("./get");
    var GET_VALID_INPUT = require("./response/get/validInput");
    
    module.exports = Model;
    
    Model.ref = jsong.ref;
    Model.atom = jsong.atom;
    Model.error = jsong.error;
    Model.pathValue = jsong.pathValue;
    
    /**
     * This callback is invoked when the Model's cache is changed.
     * @callback Model~onChange
     */
    
    /**
     * This function is invoked on every JSONGraph Error retrieved from the DataSource. This function allows Error objects
     * to be transformed before being stored in the Model's cache.
     * @callback Model~errorSelector
     * @param {Object} jsonGraphError - the JSONGraph Error object to transform before it is stored in the Model's cache.
     * @returns {Object} the JSONGraph Error object to store in the Model cache.
     */
    
    /**
     * This function is invoked every time a value in the Model cache is about to be replaced with a new value. If the
     * function returns true, the existing value is replaced with a new value and the version flag on all of the value's
     * ancestors in the tree are incremented.
     * @callback Model~comparator
     * @param {Object} existingValue - the current value in the Model cache.
     * @param {Object} newValue - the value about to be set into the Model cache.
     * @returns {Boolean} the Boolean value indicating whether the new value and the existing value are equal.
     */
    
    /**
     * @typedef {Object} Options
     * @property {DataSource} [source] A data source to retrieve and manage the {@link JSONGraph}
     * @property {JSONGraph} [cache] Initial state of the {@link JSONGraph}
     * @property {number} [maxSize] The maximum size of the cache before cache pruning is performed. The unit of this value
     * depends on the algorithm used to calculate the `$size` field on graph nodes by the backing source for the Model's
     * DataSource. If no DataSource is used, or the DataSource does not provide `$size` values, a naive algorithm is used
     * where the cache size is calculated in terms of graph node count and, for arrays and strings, element count.
     * @property {number} [collectRatio] The ratio of the maximum size to collect when the maxSize is exceeded.
     * @property {number} [maxRetries] The maximum number of times that the Model will attempt to retrieve the value from
     * its DataSource. Defaults to `3`.
     * @property {Model~errorSelector} [errorSelector] A function used to translate errors before they are returned
     * @property {Model~onChange} [onChange] A function called whenever the Model's cache is changed
     * @property {Model~comparator} [comparator] A function called whenever a value in the Model's cache is about to be
     * replaced with a new value.
     * @property {boolean} [disablePathCollapse] Disables the algorithm that collapses paths on GET requests. The algorithm
     * is enabled by default. This is a relatively computationally expensive feature.
     * @property {boolean} [disableRequestDeduplication] Disables the algorithm that deduplicates paths across in-flight GET
     * requests. The algorithm is enabled by default. This is a computationally expensive feature.
     */
    
    /**
     * A Model object is used to execute commands against a {@link JSONGraph} object. {@link Model}s can work with a local JSONGraph cache, or it can work with a remote {@link JSONGraph} object through a {@link DataSource}.
     * @constructor
     * @param {Options} [o] - a set of options to customize behavior
     */
    function Model(o) {
        var options = o || {};
        this._root = options._root || new ModelRoot(options);
        this._path = options.path || options._path || [];
        this._source = options.source || options._source;
        this._request =
            options.request || options._request || new RequestQueue(this, options.scheduler || new ImmediateScheduler());
        this._ID = ID++;
    
        if (typeof options.maxSize === "number") {
            this._maxSize = options.maxSize;
        } else {
            this._maxSize = options._maxSize || Model.prototype._maxSize;
        }
    
        if (typeof options.maxRetries === "number") {
            this._maxRetries = options.maxRetries;
        } else {
            this._maxRetries = options._maxRetries || Model.prototype._maxRetries;
        }
    
        if (typeof options.collectRatio === "number") {
            this._collectRatio = options.collectRatio;
        } else {
            this._collectRatio = options._collectRatio || Model.prototype._collectRatio;
        }
    
        if (options.boxed || options.hasOwnProperty("_boxed")) {
            this._boxed = options.boxed || options._boxed;
        }
    
        if (options.materialized || options.hasOwnProperty("_materialized")) {
            this._materialized = options.materialized || options._materialized;
        }
    
        if (typeof options.treatErrorsAsValues === "boolean") {
            this._treatErrorsAsValues = options.treatErrorsAsValues;
        } else if (options.hasOwnProperty("_treatErrorsAsValues")) {
            this._treatErrorsAsValues = options._treatErrorsAsValues;
        } else {
            this._treatErrorsAsValues = false;
        }
    
        if (typeof options.disablePathCollapse === "boolean") {
            this._enablePathCollapse = !options.disablePathCollapse;
        } else if (options.hasOwnProperty("_enablePathCollapse")) {
            this._enablePathCollapse = options._enablePathCollapse;
        } else {
            this._enablePathCollapse = true;
        }
    
        if (typeof options.disableRequestDeduplication === "boolean") {
            this._enableRequestDeduplication = !options.disableRequestDeduplication;
        } else if (options.hasOwnProperty("_enableRequestDeduplication")) {
            this._enableRequestDeduplication = options._enableRequestDeduplication;
        } else {
            this._enableRequestDeduplication = true;
        }
    
        this._useServerPaths = options._useServerPaths || false;
    
        this._allowFromWhenceYouCame = options.allowFromWhenceYouCame || options._allowFromWhenceYouCame || false;
    
        this._treatDataSourceErrorsAsJSONGraphErrors = options._treatDataSourceErrorsAsJSONGraphErrors || false;
    
        if (options.cache) {
            this.setCache(options.cache);
        }
    }
    
    Model.prototype.constructor = Model;
    
    Model.prototype._materialized = false;
    Model.prototype._boxed = false;
    Model.prototype._progressive = false;
    Model.prototype._treatErrorsAsValues = false;
    Model.prototype._maxSize = Math.pow(2, 53) - 1;
    Model.prototype._maxRetries = 3;
    Model.prototype._collectRatio = 0.75;
    Model.prototype._enablePathCollapse = true;
    Model.prototype._enableRequestDeduplication = true;
    
    /**
     * The get method retrieves several {@link Path}s or {@link PathSet}s from a {@link Model}. The get method loads each value into a JSON object and returns in a ModelResponse.
     * @function
     * @param {...PathSet} path - the path(s) to retrieve
     * @return {ModelResponse.<JSONEnvelope>} - the requested data as JSON
     */
    Model.prototype.get = require("./response/get");
    
    /**
     * _getOptimizedBoundPath is an extension point for internal users to polyfill
     * legacy soft-bind behavior, as opposed to deref (hardBind). Current falcor
     * only supports deref, and assumes _path to be a fully optimized path.
     * @function
     * @private
     * @return {Path} - fully optimized bound path for the model
     */
    Model.prototype._getOptimizedBoundPath = function _getOptimizedBoundPath() {
        return this._path ? this._path.slice() : this._path;
    };
    
    /**
     * The get method retrieves several {@link Path}s or {@link PathSet}s from a {@link Model}. The get method loads each value into a JSON object and returns in a ModelResponse.
     * @function
     * @private
     * @param {Array.<PathSet>} paths - the path(s) to retrieve
     * @return {ModelResponse.<JSONEnvelope>} - the requested data as JSON
     */
    Model.prototype._getWithPaths = require("./response/get/getWithPaths");
    
    /**
     * Sets the value at one or more places in the JSONGraph model. The set method accepts one or more {@link PathValue}s, each of which is a combination of a location in the document and the value to place there.  In addition to accepting  {@link PathValue}s, the set method also returns the values after the set operation is complete.
     * @function
     * @return {ModelResponse.<JSONEnvelope>} - an {@link Observable} stream containing the values in the JSONGraph model after the set was attempted
     */
    Model.prototype.set = require("./response/set");
    
    /**
     * The preload method retrieves several {@link Path}s or {@link PathSet}s from a {@link Model} and loads them into the Model cache.
     * @function
     * @param {...PathSet} path - the path(s) to retrieve
     * @return {ModelResponse.<JSONEnvelope>} - a ModelResponse that completes when the data has been loaded into the cache.
     */
    Model.prototype.preload = function preload() {
        var out = validateInput(arguments, GET_VALID_INPUT, "preload");
        if (out !== true) {
            return new ModelResponse(function(o) {
                o.onError(out);
            });
        }
        var args = Array.prototype.slice.call(arguments);
        var self = this;
        return new ModelResponse(function(obs) {
            return self.get.apply(self, args).subscribe(
                function() {},
                function(err) {
                    obs.onError(err);
                },
                function() {
                    obs.onCompleted();
                }
            );
        });
    };
    
    /**
     * Invokes a function in the JSON Graph.
     * @function
     * @param {Path} functionPath - the path to the function to invoke
     * @param {Array.<Object>} args - the arguments to pass to the function
     * @param {Array.<PathSet>} refPaths - the paths to retrieve from the JSON Graph References in the message returned from the function
     * @param {Array.<PathSet>} extraPaths - additional paths to retrieve after successful function execution
     * @return {ModelResponse.<JSONEnvelope> - a JSONEnvelope contains the values returned from the function
     */
    Model.prototype.call = function call() {
        var args;
        var argsIdx = -1;
        var argsLen = arguments.length;
        args = new Array(argsLen);
        while (++argsIdx < argsLen) {
            var arg = arguments[argsIdx];
            args[argsIdx] = arg;
            var argType = typeof arg;
            if (
                (argsIdx > 1 && !Array.isArray(arg)) ||
                (argsIdx === 0 && !Array.isArray(arg) && argType !== "string") ||
                (argsIdx === 1 && !Array.isArray(arg) && !isPrimitive(arg))
            ) {
                /* eslint-disable no-loop-func */
                return new ModelResponse(function(o) {
                    o.onError(new Error("Invalid argument"));
                });
                /* eslint-enable no-loop-func */
            }
        }
    
        return new CallResponse(this, args[0], args[1], args[2], args[3]);
    };
    
    /**
     * The invalidate method synchronously removes several {@link Path}s or {@link PathSet}s from a {@link Model} cache.
     * @function
     * @param {...PathSet} path - the  paths to remove from the {@link Model}'s cache.
     */
    Model.prototype.invalidate = function invalidate() {
        var args;
        var argsIdx = -1;
        var argsLen = arguments.length;
        args = [];
        while (++argsIdx < argsLen) {
            args[argsIdx] = pathSyntax.fromPath(arguments[argsIdx]);
            if (!Array.isArray(args[argsIdx]) || !args[argsIdx].length) {
                throw new Error("Invalid argument");
            }
        }
    
        // creates the obs, subscribes and will throw the errors if encountered.
        new InvalidateResponse(this, args).subscribe(noOp, function(e) {
            throw e;
        });
    };
    
    /**
     * Returns a new {@link Model} bound to a location within the {@link
     * JSONGraph}. The bound location is never a {@link Reference}: any {@link
     * Reference}s encountered while resolving the bound {@link Path} are always
     * replaced with the {@link Reference}s target value. For subsequent operations
     * on the {@link Model}, all paths will be evaluated relative to the bound
     * path. Deref allows you to:
     * - Expose only a fragment of the {@link JSONGraph} to components, rather than
     *   the entire graph
     * - Hide the location of a {@link JSONGraph} fragment from components
     * - Optimize for executing multiple operations and path looksup at/below the
     *   same location in the {@link JSONGraph}
     * @method
     * @param {Object} responseObject - an object previously retrieved from the
     * Model
     * @return {Model} - the dereferenced {@link Model}
     * @example
    var Model = falcor.Model;
    var model = new Model({
      cache: {
        users: [
          Model.ref(["usersById", 32])
        ],
        usersById: {
          32: {
            name: "Steve",
            surname: "McGuire"
          }
        }
      }
    });
    
    model.
        get(['users', 0, 'name']).
        subscribe(function(jsonEnv) {
            var userModel = model.deref(jsonEnv.json.users[0]);
            console.log(model.getPath());
            console.log(userModel.getPath());
       });
    });
    
    // prints the following:
    // []
    // ["usersById", 32] - because userModel refers to target of reference at ["users", 0]
     */
    Model.prototype.deref = require("./deref");
    
    /**
     * A dereferenced model can become invalid when the reference from which it was
     * built has been removed/collected/expired/etc etc.  To fix the issue, a from
     * the parent request should be made (no parent, then from the root) for a valid
     * path and re-dereference performed to update what the model is bound too.
     *
     * @method
     * @private
     * @return {Boolean} - If the currently deref'd model is still considered a
     * valid deref.
     */
    Model.prototype._hasValidParentReference = require("./deref/hasValidParentReference");
    
    /**
     * Get data for a single {@link Path}.
     * @param {Path} path - the path to retrieve
     * @return {Observable.<*>} - the value for the path
     * @example
     var model = new falcor.Model({source: new HttpDataSource("/model.json") });
    
     model.
         getValue('user.name').
         subscribe(function(name) {
             console.log(name);
         });
    
     // The code above prints "Jim" to the console.
     */
    Model.prototype.getValue = require("./get/getValue");
    
    /**
     * Set value for a single {@link Path}.
     * @param {Path} path - the path to set
     * @param {Object} value - the value to set
     * @return {Observable.<*>} - the value for the path
     * @example
     var model = new falcor.Model({source: new HttpDataSource("/model.json") });
    
     model.
         setValue('user.name', 'Jim').
         subscribe(function(name) {
             console.log(name);
         });
    
     // The code above prints "Jim" to the console.
     */
    Model.prototype.setValue = require("./set/setValue");
    
    // TODO: Does not throw if given a PathSet rather than a Path, not sure if it should or not.
    // TODO: Doc not accurate? I was able to invoke directly against the Model, perhaps because I don't have a data source?
    // TODO: Not clear on what it means to "retrieve objects in addition to JSONGraph values"
    /**
     * Synchronously retrieves a single path from the local {@link Model} only and will not retrieve missing paths from the {@link DataSource}. This method can only be invoked when the {@link Model} does not have a {@link DataSource} or from within a selector function. See {@link Model.prototype.get}. The getValueSync method differs from the asynchronous get methods (ex. get, getValues) in that it can be used to retrieve objects in addition to JSONGraph values.
     * @method
     * @private
     * @arg {Path} path - the path to retrieve
     * @return {*} - the value for the specified path
     */
    Model.prototype._getValueSync = require("./get/sync");
    
    /**
     * @private
     */
    Model.prototype._setValueSync = require("./set/sync");
    
    /**
     * @private
     */
    Model.prototype._derefSync = require("./deref/sync");
    
    /**
     * Set the local cache to a {@link JSONGraph} fragment. This method can be a useful way of mocking a remote document, or restoring the local cache from a previously stored state.
     * @param {JSONGraph} jsonGraph - the {@link JSONGraph} fragment to use as the local cache
     */
    Model.prototype.setCache = function modelSetCache(cacheOrJSONGraphEnvelope) {
        var cache = this._root.cache;
        if (cacheOrJSONGraphEnvelope !== cache) {
            var modelRoot = this._root;
            var boundPath = this._path;
            this._path = [];
            this._root.cache = {};
            if (typeof cache !== "undefined") {
                collectLru(modelRoot, modelRoot.expired, getSize(cache), 0);
            }
            var out;
            if (isJSONGraphEnvelope(cacheOrJSONGraphEnvelope)) {
                out = setJSONGraphs(this, [cacheOrJSONGraphEnvelope])[0];
            } else if (isJSONEnvelope(cacheOrJSONGraphEnvelope)) {
                out = setCache(this, [cacheOrJSONGraphEnvelope])[0];
            } else if (isObject(cacheOrJSONGraphEnvelope)) {
                out = setCache(this, [{ json: cacheOrJSONGraphEnvelope }])[0];
            }
    
            // performs promotion without producing output.
            if (out) {
                get.getWithPathsAsPathMap(this, out, []);
            }
            this._path = boundPath;
        } else if (typeof cache === "undefined") {
            this._root.cache = {};
        }
        return this;
    };
    
    /**
     * Get the local {@link JSONGraph} cache. This method can be a useful to store the state of the cache.
     * @param {...Array.<PathSet>} [pathSets] - The path(s) to retrieve. If no paths are specified, the entire {@link JSONGraph} is returned.
     * @return {JSONGraph} all of the {@link JSONGraph} data in the {@link Model} cache.
     * @example
     // Storing the boxshot of the first 10 titles in the first 10 genreLists to local storage.
     localStorage.setItem('cache', JSON.stringify(model.getCache("genreLists[0...10][0...10].boxshot")));
     */
    Model.prototype.getCache = function _getCache() {
        var paths = Array.prototype.slice.call(arguments);
        if (paths.length === 0) {
            return getCache(this._root.cache);
        }
    
        var result = [{}];
        var path = this._path;
        get.getWithPathsAsJSONGraph(this, paths, result);
        this._path = path;
        return result[0].jsonGraph;
    };
    
    /**
     * Reset cache maxSize. When the new maxSize is smaller than the old force a collect.
     * @param {Number} maxSize - the new maximum cache size
     */
    Model.prototype._setMaxSize = function setMaxSize(maxSize) {
        var oldMaxSize = this._maxSize;
        this._maxSize = maxSize;
        if (maxSize < oldMaxSize) {
            var modelRoot = this._root;
            var modelCache = modelRoot.cache;
            // eslint-disable-next-line no-cond-assign
            var currentVersion = modelCache.$_version;
            collectLru(
                modelRoot,
                modelRoot.expired,
                getSize(modelCache),
                this._maxSize,
                this._collectRatio,
                currentVersion
            );
        }
    };
    
    /**
     * Retrieves a number which is incremented every single time a value is changed underneath the Model or the object at an optionally-provided Path beneath the Model.
     * @param {Path?} path - a path at which to retrieve the version number
     * @return {Number} a version number which changes whenever a value is changed underneath the Model or provided Path
     */
    Model.prototype.getVersion = function getVersion(pathArg) {
        var path = (pathArg && pathSyntax.fromPath(pathArg)) || [];
        if (Array.isArray(path) === false) {
            throw new Error("Model#getVersion must be called with an Array path.");
        }
        if (this._path.length) {
            path = this._path.concat(path);
        }
        return this._getVersion(this, path);
    };
    
    Model.prototype._syncCheck = function syncCheck(name) {
        if (Boolean(this._source) && this._root.syncRefCount <= 0 && this._root.unsafeMode === false) {
            throw new Error("Model#" + name + " may only be called within the context of a request selector.");
        }
        return true;
    };
    
    /* eslint-disable guard-for-in */
    Model.prototype._clone = function cloneModel(opts) {
        var clone = new this.constructor(this);
        for (var key in opts) {
            var value = opts[key];
            if (value === "delete") {
                delete clone[key];
            } else {
                clone[key] = value;
            }
        }
        clone.setCache = void 0;
        return clone;
    };
    /* eslint-enable */
    
    /**
     * Returns a clone of the {@link Model} that enables batching. Within the configured time period,
     * paths for get operations are collected and sent to the {@link DataSource} in a batch. Batching
     * can be more efficient if the {@link DataSource} access the network, potentially reducing the
     * number of HTTP requests to the server.
     *
     * @param {?Scheduler|number} schedulerOrDelay - Either a {@link Scheduler} that determines when to
     * send a batch to the {@link DataSource}, or the number in milliseconds to collect a batch before
     * sending to the {@link DataSource}. If this parameter is omitted, then batch collection ends at
     * the end of the next tick.
     * @return {Model} a Model which schedules a batch of get requests to the DataSource.
     */
    Model.prototype.batch = function batch(schedulerOrDelay) {
        var scheduler;
        if (typeof schedulerOrDelay === "number") {
            scheduler = new TimeoutScheduler(Math.round(Math.abs(schedulerOrDelay)));
        } else if (!schedulerOrDelay || !schedulerOrDelay.schedule) {
            scheduler = new TimeoutScheduler(1);
        } else {
            scheduler = schedulerOrDelay;
        }
    
        var clone = this._clone();
        clone._request = new RequestQueue(clone, scheduler);
    
        return clone;
    };
    
    /**
     * Returns a clone of the {@link Model} that disables batching. This is the default mode. Each get operation will be executed on the {@link DataSource} separately.
     * @name unbatch
     * @memberof Model.prototype
     * @function
     * @return {Model} a {@link Model} that batches requests of the same type and sends them to the data source together
     */
    Model.prototype.unbatch = function unbatch() {
        var clone = this._clone();
        clone._request = new RequestQueue(clone, new ImmediateScheduler());
        return clone;
    };
    
    /**
     * Returns a clone of the {@link Model} that treats errors as values. Errors will be reported in the same callback used to report data. Errors will appear as objects in responses, rather than being sent to the {@link Observable~onErrorCallback} callback of the {@link ModelResponse}.
     * @return {Model}
     */
    Model.prototype.treatErrorsAsValues = function treatErrorsAsValues() {
        return this._clone({
            _treatErrorsAsValues: true
        });
    };
    
    /**
     * Adapts a Model to the {@link DataSource} interface.
     * @return {DataSource}
     * @example
    var model =
        new falcor.Model({
            cache: {
                user: {
                    name: "Steve",
                    surname: "McGuire"
                }
            }
        }),
        proxyModel = new falcor.Model({ source: model.asDataSource() });
    
    // Prints "Steve"
    proxyModel.getValue("user.name").
        then(function(name) {
            console.log(name);
        });
     */
    Model.prototype.asDataSource = function asDataSource() {
        return new ModelDataSourceAdapter(this);
    };
    
    Model.prototype._materialize = function materialize() {
        return this._clone({
            _materialized: true
        });
    };
    
    Model.prototype._dematerialize = function dematerialize() {
        return this._clone({
            _materialized: "delete"
        });
    };
    
    /**
     * Returns a clone of the {@link Model} that boxes values returning the wrapper ({@link Atom}, {@link Reference}, or {@link Error}), rather than the value inside it. This allows any metadata attached to the wrapper to be inspected.
     * @return {Model}
     */
    Model.prototype.boxValues = function boxValues() {
        return this._clone({
            _boxed: true
        });
    };
    
    /**
     * Returns a clone of the {@link Model} that unboxes values, returning the value inside of the wrapper ({@link Atom}, {@link Reference}, or {@link Error}), rather than the wrapper itself. This is the default mode.
     * @return {Model}
     */
    Model.prototype.unboxValues = function unboxValues() {
        return this._clone({
            _boxed: "delete"
        });
    };
    
    /**
     * Returns a clone of the {@link Model} that only uses the local {@link JSONGraph} and never uses a {@link DataSource} to retrieve missing paths.
     * @return {Model}
     */
    Model.prototype.withoutDataSource = function withoutDataSource() {
        return this._clone({
            _source: "delete"
        });
    };
    
    Model.prototype.toJSON = function toJSON() {
        return {
            $type: "ref",
            value: this._path
        };
    };
    
    /**
     * Returns the {@link Path} to the object within the JSON Graph that this Model references.
     * @return {Path}
     * @example
    var Model = falcor.Model;
    var model = new Model({
      cache: {
        users: [
          Model.ref(["usersById", 32])
        ],
        usersById: {
          32: {
            name: "Steve",
            surname: "McGuire"
          }
        }
      }
    });
    
    model.
        get(['users', 0, 'name']).
        subscribe(function(jsonEnv) {
            var userModel = model.deref(jsonEnv.json.users[0]);
            console.log(model.getPath());
            console.log(userModel.getPath());
       });
    });
    
    // prints the following:
    // []
    // ["usersById", 32] - because userModel refers to target of reference at ["users", 0]
     */
    Model.prototype.getPath = function getPath() {
        return this._path ? this._path.slice() : this._path;
    };
    
    /**
     * This one is actually private.  I would not use this without talking to
     * jhusain, sdesai, or michaelbpaulson (github).
     * @private
     */
    Model.prototype._fromWhenceYouCame = function fromWhenceYouCame(allow) {
        return this._clone({
            _allowFromWhenceYouCame: allow === undefined ? true : allow
        });
    };
    
    Model.prototype._getBoundValue = require("./get/getBoundValue");
    Model.prototype._getVersion = require("./get/getVersion");
    
    Model.prototype._getPathValuesAsPathMap = get.getWithPathsAsPathMap;
    Model.prototype._getPathValuesAsJSONG = get.getWithPathsAsJSONGraph;
    
    Model.prototype._setPathValues = require("./set/setPathValues");
    Model.prototype._setPathMaps = require("./set/setPathMaps");
    Model.prototype._setJSONGs = require("./set/setJSONGraphs");
    Model.prototype._setCache = require("./set/setPathMaps");
    
    Model.prototype._invalidatePathValues = require("./invalidate/invalidatePathSets");
    Model.prototype._invalidatePathMaps = require("./invalidate/invalidatePathMaps");
    
    ================================================ FILE: doc/ModelResponse.html ================================================ --- layout: api-page title: "Class: ModelResponse" id: api ---

    ModelResponse

    new ModelResponse()

    A ModelResponse is a container for the results of a get, set, or call operation performed on a Model. The ModelResponse provides methods which can be used to specify the output format of the data retrieved from a Model, as well as how that data is delivered.

    Source:
    response/ModelResponse.js, line 5

    Extends

    Methods

    forEach(onNextnullable, onErrornullable, onCompletednullable) → {Subscription}

    The forEach method is a synonym for Observable.prototype.subscribe and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us. These values can be received using either callbacks or an Observer object.

    Parameters:
    Name & Attributes Type Description
    onNext
    nullable
    Observable~onNextCallback

    a callback that accepts the next value in the stream of values

    onError
    nullable
    Observable~onErrorCallback

    a callback that accepts an error that occurred while evaluating the operation underlying the Observable stream

    onCompleted
    nullable
    Observable~onCompletedCallback

    a callback that is invoked when the Observable stream has ended, and the Observable~onNextCallback will not receive any more values

    Overrides:
    Observable#forEach
    Source:
    typedefs/Observable.js, line 6
    Return Type:
    Subscription

    progressively() → {ModelResponse.<JSONEnvelope>}

    The progressively method breaks the response up into two parts: the data immediately available in the Model cache, and the data in the Model cache after the missing data has been retrieved from the DataSource. The progressively method creates a ModelResponse that immediately returns the requested data that is available in the Model cache. If any requested paths are not available in the cache, the ModelResponse will send another JSON message with all of the requested data after it has been retrieved from the DataSource.

    Source:
    response/ModelResponse.js, line 22
    Returns:

    the values found at the requested paths.

    Return Type:
    ModelResponse.<JSONEnvelope>
    Example
    var dataSource = (new falcor.Model({
      cache: {
        user: {
          name: "Steve",
          surname: "McGuire",
          age: 31
        }
      }
    })).asDataSource();
    
    var model = new falcor.Model({
      source: dataSource,
      cache: {
        user: {
          name: "Steve",
          surname: "McGuire"
        }
      }
    });
    
    model.
      get(["user",["name", "surname", "age"]]).
      progressively().
      // this callback will be invoked twice, once with the data in the
      // Model cache, and again with the additional data retrieved from the DataSource.
      subscribe(function(json){
        console.log(JSON.stringify(json,null,4));
      });
    
    // prints...
    // {
    //     "json": {
    //         "user": {
    //             "name": "Steve",
    //             "surname": "McGuire"
    //         }
    //     }
    // }
    // ...and then prints...
    // {
    //     "json": {
    //         "user": {
    //             "name": "Steve",
    //             "surname": "McGuire",
    //             "age": 31
    //         }
    //     }
    // }

    subscribe(onNextnullable, onErrornullable, onCompletednullable) → {Subscription}

    The subscribe method is a synonym for Observable.prototype.forEach and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us. These values can be received using either callbacks or an Observer object.

    Parameters:
    Name & Attributes Type Description
    onNext
    nullable
    Observable~onNextCallback

    a callback that accepts the next value in the stream of values

    onError
    nullable
    Observable~onErrorCallback

    a callback that accepts an error that occurred while evaluating the operation underlying the Observable stream

    onCompleted
    nullable
    Observable~onCompletedCallback

    a callback that is invoked when the Observable stream has ended, and the Observable~onNextCallback will not receive any more values

    Overrides:
    Observable#subscribe
    Source:
    typedefs/Observable.js, line 17
    Return Type:
    Subscription
    ================================================ FILE: doc/ModelResponseObserver.html ================================================ --- layout: api-page title: "Class: ModelResponseObserver" id: api ---

    ModelResponseObserver

    new ModelResponseObserver()

    A ModelResponseObserver conform to the Observable's Observer contract. It accepts either an Observer or three optional callbacks which correspond to the Observer methods onNext, onError, and onCompleted. The ModelResponseObserver wraps an Observer to enforce a variety of different invariants including:

    1. onError callback is only called once.
    2. onCompleted callback is only called once.
    Source:
    response/ModelResponseObserver.js, line 3
    ================================================ FILE: doc/Observable.html ================================================ --- layout: api-page title: "Class: Observable" id: api ---

    Observable

    new Observable()

    Source:
    typedefs/Observable.js, line 2

    Methods

    forEach(onNextnullable, onErrornullable, onCompletednullable) → {Subscription}

    The forEach method is a synonym for Observable.prototype.subscribe and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us. These values can be received using either callbacks or an Observer object.

    Parameters:
    Name & Attributes Type Description
    onNext
    nullable
    Observable~onNextCallback

    a callback that accepts the next value in the stream of values

    onError
    nullable
    Observable~onErrorCallback

    a callback that accepts an error that occurred while evaluating the operation underlying the Observable stream

    onCompleted
    nullable
    Observable~onCompletedCallback

    a callback that is invoked when the Observable stream has ended, and the Observable~onNextCallback will not receive any more values

    Source:
    typedefs/Observable.js, line 6
    Return Type:
    Subscription

    subscribe(onNextnullable, onErrornullable, onCompletednullable) → {Subscription}

    The subscribe method is a synonym for Observable.prototype.forEach and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us. These values can be received using either callbacks or an Observer object.

    Parameters:
    Name & Attributes Type Description
    onNext
    nullable
    Observable~onNextCallback

    a callback that accepts the next value in the stream of values

    onError
    nullable
    Observable~onErrorCallback

    a callback that accepts an error that occurred while evaluating the operation underlying the Observable stream

    onCompleted
    nullable
    Observable~onCompletedCallback

    a callback that is invoked when the Observable stream has ended, and the Observable~onNextCallback will not receive any more values

    Source:
    typedefs/Observable.js, line 17
    Return Type:
    Subscription

    Type Definitions

    onCompletedCallback()

    This callback is invoked when the Observable stream ends. When this callback is invoked the Observable stream has ended, and therefore the Observable~onNextCallback will not receive any more values.

    Source:
    typedefs/Observable.js, line 40

    onErrorCallback(error)

    This callback accepts an error that occurred while evaluating the operation underlying the Observable stream. When this callback is invoked, the Observable stream ends and no more values will be received by the Observable~onNextCallback.

    Parameters:
    Name Type Description
    error Error

    the error that occurred while evaluating the operation underlying the Observable

    Source:
    typedefs/Observable.js, line 34

    onNextCallback(value)

    This callback accepts a value that was emitted while evaluating the operation underlying the Observable stream.

    Parameters:
    Name Type Description
    value Object

    the value that was emitted while evaluating the operation underlying the Observable

    Source:
    typedefs/Observable.js, line 28
    ================================================ FILE: doc/Subscription.html ================================================ --- layout: api-page title: "Class: Subscription" id: api ---

    Subscription

    ================================================ FILE: doc/ToEsSubscriptionAdapter.html ================================================ --- layout: api-page title: "Class: ToEsSubscriptionAdapter" id: api ---

    ToEsSubscriptionAdapter

    new ToEsSubscriptionAdapter()

    ToEsSubscriptionAdapter is an adpater from the Rx 2 subscription to the ES subscription

    Source:
    toEsObservable.js, line 27
    ================================================ FILE: doc/get_getCache.js.html ================================================ --- layout: api-page title: "get/getCache.js" id: api ---

    get/getCache.js

    var isInternalKey = require("./../support/isInternalKey");
    
    /**
     * decends and copies the cache.
     */
    module.exports = function getCache(cache) {
        var out = {};
        _copyCache(cache, out);
    
        return out;
    };
    
    function cloneBoxedValue(boxedValue) {
        var clonedValue = {};
    
        var keys = Object.keys(boxedValue);
        var key;
        var i;
        var l;
    
        for (i = 0, l = keys.length; i < l; i++) {
            key = keys[i];
    
            if (!isInternalKey(key)) {
                clonedValue[key] = boxedValue[key];
            }
        }
    
        return clonedValue;
    }
    
    function _copyCache(node, out, fromKey) {
        // copy and return
    
        Object.
            keys(node).
            filter(function(k) {
                // Its not an internal key and the node has a value.  In the cache
                // there are 3 possibilities for values.
                // 1: A branch node.
                // 2: A $type-value node.
                // 3: undefined
                // We will strip out 3
                return !isInternalKey(k) && node[k] !== undefined;
            }).
            forEach(function(key) {
                var cacheNext = node[key];
                var outNext = out[key];
    
                if (!outNext) {
                    outNext = out[key] = {};
                }
    
                // Paste the node into the out cache.
                if (cacheNext.$type) {
                    var isObject = cacheNext.value && typeof cacheNext.value === "object";
                    var isUserCreatedcacheNext = !cacheNext.$_modelCreated;
                    var value;
                    if (isObject || isUserCreatedcacheNext) {
                        value = cloneBoxedValue(cacheNext);
                    } else {
                        value = cacheNext.value;
                    }
    
                    out[key] = value;
                    return;
                }
    
                _copyCache(cacheNext, outNext, key);
            });
    }
    
    ================================================ FILE: doc/global.html ================================================ --- layout: api-page title: "Global" id: api ---

    Global

    Methods

    arrayConcatElement()

    Create a new array consistent of a1 plus an additional element. Avoids the unnecessary array allocation when using a1.concat([element]).

    Source:
    request/complement.js, line 207

    arrayConcatSlice()

    Create a new array consisting of a1 + a subset of a2. Avoids allocating an extra array by calling slice on a2.

    Source:
    request/complement.js, line 179

    arrayConcatSlice2()

    Create a new array consisting of a1 + a2 + a subset of a3. Avoids allocating an extra array by calling slice on a3.

    Source:
    request/complement.js, line 193

    findPartialIntersections()

    Recursive function to calculate intersection and complement paths in 2 given pathsets at a given depth.

    Parameters:

    • requestedPath: full requested path set (can include ranges)
    • optimizedPath: corresponding optimized path (can include ranges)
    • requestTree: path tree for in-flight request, against which to dedupe

    Returns a 3-tuple consisting of

    • the intersection of requested paths with requestTree
    • the complement of optimized paths with requestTree
    • the complement of corresponding requested paths with requestTree

    Example scenario:

    • requestedPath: ['lolomo', 0, 0, 'tags', { from: 0, to: 2 }]
    • optimizedPath: ['videosById', 11, 'tags', { from: 0, to: 2 }]
    • requestTree: { videosById: 11: { tags: { 0: null, 1: null }}}

    This returns: [ [['lolomo', 0, 0, 'tags', 0], ['lolomo', 0, 0, 'tags', 1]], [['videosById', 11, 'tags', 2]], [['lolomo', 0, 0, 'tags', 2]] ]

    Source:
    request/complement.js, line 64

    GetRequestV2(scheduler, requestQueue, attemptCount)

    Creates a new GetRequest. This GetRequest takes a scheduler and the request queue. Once the scheduler fires, all batched requests will be sent to the server. Upon request completion, the data is merged back into the cache and all callbacks are notified.

    Parameters:
    Name Type Description
    scheduler Scheduler
    requestQueue RequestQueueV2
    attemptCount number
    Source:
    request/GetRequestV2.js, line 24

    RequestQueueV2(model, scheduler)

    The request queue is responsible for queuing the operations to the model"s dataSource.

    Parameters:
    Name Type Description
    model Model
    scheduler Scheduler
    Source:
    request/RequestQueueV2.js, line 13

    Type Definitions

    Atom

    An atom allows you to treat a JSON value as atomic regardless of its type, ensuring that a JSON object or array is always returned in its entirety. The JSON value must be treated as immutable. Atoms can also be used to associate metadata with a JSON value. This metadata can be used to influence the way values are handled.

    Type:
    Object
    Properties
    Name Type Attributes Description
    $type String

    the $type must be "atom"

    value *

    the immutable JSON value

    $expires number <optional>

    the time to expire in milliseconds

    • positive number: expires in milliseconds since epoch
    • negative number: expires relative to when the Atom is merged into the JSONGraph
    • number 1: never expires
    Source:
    typedefs/Atom.js, line 1
    Example
    // Atom with number value, expiring in 2 seconds
     {
        $type: "atom",
        value: 5
        $expires: -2000
     }
     // Atom with Object value that never expires
     {
        $type: "atom",
        value: {
            foo: 5,
            bar: "baz"
        },
        $expires: 1
     }

    JSONEnvelope

    An envelope that wraps a JSON object.

    Type:
    Object
    Properties
    Name Type Description
    json JSON

    a JSON object

    Source:
    typedefs/JSONEnvelope.js, line 1
    Example
    var model = new falcor.Model();
     model.set({
        json: {
          name: "Steve",
          surname: "McGuire"
        }
     }).then(function(jsonEnvelope) {
        console.log(jsonEnvelope);
     });

    JSONGraph

    JavaScript Object Notation Graph (JSONGraph) is a notation for expressing graphs in JSON. For more information, see the JSONGraph Guide.

    Type:
    Object
    Source:
    typedefs/JSONGraph.js, line 1
    Example
    var $ref = falcor.ref;
     // JSONGraph model modeling a list of film genres, each of which contains the same title.
     {
        // list of user's genres, modeled as a map with ordinal keys
        "genreLists": {
            "0": $ref('genresById[123]'),
            "1": $ref('genresById[522]'),
            "length": 2
        },
        // map of all genres, organized by ID
        "genresById": {
            // genre list modeled as map with ordinal keys
            "123": {
                "name": "Drama",
                "0": $ref('titlesById[23]'),
                "1": $ref('titlesById[99]'),
                "length": 2
            },
            // genre list modeled as map with ordinal keys
            "522": {
                "name": "Comedy",
                "0": $ref('titlesById[23]'),
                "1": $ref('titlesById[44]'),
                "length": 2
            }
        },
        // map of all titles, organized by ID
        "titlesById": {
           "99": {
                "name": "House of Cards",
                "rating": 5
            },
            "23": {
                "name": "Orange is the New Black",
                "rating": 5
            },
            "44": {
                "name": "Arrested Development",
                "rating": 5
            }
        }
    }

    JSONGraphEnvelope

    An envelope that wraps a JSONGraph fragment.

    Type:
    Object
    Properties
    Name Type Attributes Description
    jsonGraph JSONGraph

    a JSONGraph fragment

    paths Array.<PathSet> <nullable>

    the paths to the values in the JSONGraph fragment

    invalidated Array.<PathSet> <nullable>

    the paths to invalidate in the Model

    Source:
    typedefs/JSONGraphEnvelope.js, line 1
    Example
    var $ref = falcor.ref;
    var model = new falcor.Model();
    model.set({
      paths: [
        ["todos", [0,1], ["name","done"]]
      ],
      jsonGraph: {
        todos: [
          $ref("todosById[12]"),
          $ref("todosById[15]")
        ],
        todosById: {
          12: {
            name: "go to the ATM",
            done: false
          },
          15: {
            name: "buy milk",
            done: false
          }
        }
      },
    }).then(function(jsonEnvelope) {
      console.log(JSON.stringify(jsonEnvelope, null, 4));
    });
    
    // prints...
    // {
    //   json: {
    //     todos: {
    //       0: {
    //         name: "go to the ATM",
    //         done: false
    //       },
    //       1: {
    //         name: "buy milk",
    //         done: false
    //       }
    //     }
    //   }
    // }

    Key

    A part of a Path that can be any JSON value type. All types are coerced to string, except null. This makes the number 1 and the string "1" equivalent.

    Type:
    string or number or boolean or null
    Source:
    typedefs/Key.js, line 1

    KeySet

    A part of a PathSet that can be either a Key, Range, or Array of either.

    Type:
    Key or Range or Array.<(Key|Range)>
    Source:
    typedefs/KeySet.js, line 1

    Options

    Type:
    Object
    Properties
    Name Type Attributes Description
    source DataSource <optional>

    A data source to retrieve and manage the JSONGraph

    cache JSONGraph <optional>

    Initial state of the JSONGraph

    maxSize number <optional>

    The maximum size of the cache before cache pruning is performed. The unit of this value depends on the algorithm used to calculate the $size field on graph nodes by the backing source for the Model's DataSource. If no DataSource is used, or the DataSource does not provide $size values, a naive algorithm is used where the cache size is calculated in terms of graph node count and, for arrays and strings, element count.

    collectRatio number <optional>

    The ratio of the maximum size to collect when the maxSize is exceeded.

    maxRetries number <optional>

    The maximum number of times that the Model will attempt to retrieve the value from its DataSource. Defaults to 3.

    errorSelector Model~errorSelector <optional>

    A function used to translate errors before they are returned

    onChange Model~onChange <optional>

    A function called whenever the Model's cache is changed

    comparator Model~comparator <optional>

    A function called whenever a value in the Model's cache is about to be replaced with a new value.

    disablePathCollapse boolean <optional>

    Disables the algorithm that collapses paths on GET requests. The algorithm is enabled by default. This is a relatively computationally expensive feature.

    disableRequestDeduplication boolean <optional>

    Disables the algorithm that deduplicates paths across in-flight GET requests. The algorithm is enabled by default. This is a computationally expensive feature.

    Source:
    Model.js, line 61

    Path

    An ordered list of Keys that point to a value in a JSONGraph.

    Type:
    Array.<Key>
    Source:
    typedefs/Path.js, line 1
    Example
    // Points to the name of product 1234
     ["productsById", "1234", "name"]

    PathSet

    An ordered list of KeySets that point to location(s) in the JSONGraph. It enables pointing to multiple locations in a more terse format than a set of Paths and is generally more efficient to evaluate.

    Type:
    Array.<KeySet>
    Source:
    typedefs/PathSet.js, line 1
    Example
    // Points to the name and price of products 1234 and 5678
     ["productsById", ["1234", "5678"], ["name", "price"]]

    PathValue

    A wrapper around a path and its value.

    Type:
    Object
    Properties
    Name Type Attributes Description
    path PathSet

    the path to a location in the JSONGraph

    value * <nullable>

    the value of that path

    Source:
    typedefs/PathValue.js, line 1
    Example
    {
    	path: ["productsById", "1234", "name"],
    	value: "ABC"
     }

    Range

    Describe a range of integers. Must contain either a "to" or "length" property.

    Type:
    Object
    Properties
    Name Type Attributes Default Description
    from number <optional>
    0

    the lower bound of the range (inclusive)

    to number <nullable>

    the upper bound of the range (inclusive). Must be >= to the "from" value

    length number <nullable>

    the length of the range. Must be >= 0

    Source:
    typedefs/Range.js, line 1
    Example
    // The following range specifies the numbers 0, 1, and 2
     {from: 0, to: 2}
     // The following range specifies the numbers 1 and 2
     {from: 1, length: 2}
    ================================================ FILE: doc/index.html ================================================ --- layout: api-page title: "Home" id: api ---

    ================================================ FILE: doc/index.js.html ================================================ --- layout: api-page title: "index.js" id: api ---

    index.js

    "use strict";
    
    function falcor(opts) {
        return new falcor.Model(opts);
    }
    
    /**
     * A filtering method for keys from a falcor json response.  The only gotcha
     * to this method is when the incoming json is undefined, then undefined will
     * be returned.
     *
     * @public
     * @param {Object} json - The json response from a falcor model.
     * @returns {Array} - the keys that are in the model response minus the deref
     * _private_ meta data.
     */
    falcor.keys = function getJSONKeys(json) {
        if (!json) {
            return undefined;
        }
    
        return Object.
            keys(json).
            filter(function(key) {
                return key !== "$__path";
            });
    };
    
    module.exports = falcor;
    
    falcor.Model = require("./Model");
    
    ================================================ FILE: doc/internal_index.js.html ================================================ --- layout: api-page title: "internal/index.js" id: api ---

    internal/index.js

    /**
     * The list of internal keys.  Instead of a bunch of little files,
     * have them as one exports.  This makes the bundling overhead smaller!
     */
    module.exports = {
        // eslint-disable-next-line camelcase
        $__toReference: "$__toReference",
        // eslint-disable-next-line camelcase
        $__path: "$__path",
        // eslint-disable-next-line camelcase
        $__refPath: "$__refPath"
    };
    
    ================================================ FILE: doc/invalidate_invalidatePathMaps.js.html ================================================ --- layout: api-page title: "invalidate/invalidatePathMaps.js" id: api ---

    invalidate/invalidatePathMaps.js

    var createHardlink = require("../support/createHardlink");
    var __prefix = require("./../internal/reservedPrefix");
    
    var $ref = require("./../types/ref");
    
    var getBoundValue = require("./../get/getBoundValue");
    
    var promote = require("./../lru/promote");
    var getSize = require("./../support/getSize");
    var hasOwn = require("./../support/hasOwn");
    var isObject = require("./../support/isObject");
    var isExpired = require("./../support/isExpired");
    var isFunction = require("./../support/isFunction");
    var isPrimitive = require("./../support/isPrimitive");
    var expireNode = require("./../support/expireNode");
    var incrementVersion = require("./../support/incrementVersion");
    var updateNodeAncestors = require("./../support/updateNodeAncestors");
    var removeNodeAndDescendants = require("./../support/removeNodeAndDescendants");
    
    /**
     * Sets a list of PathMaps into a JSON Graph.
     * @function
     * @param {Object} model - the Model for which to insert the PathMaps.
     * @param {Array.<PathMapEnvelope>} pathMapEnvelopes - the a list of @PathMapEnvelopes to set.
     */
    
    module.exports = function invalidatePathMaps(model, pathMapEnvelopes) {
    
        var modelRoot = model._root;
        var lru = modelRoot;
        var expired = modelRoot.expired;
        var version = incrementVersion();
        var bound = model._path;
        var cache = modelRoot.cache;
        var node = bound.length ? getBoundValue(model, bound).value : cache;
        var parent = node.$_parent || cache;
        var initialVersion = cache.$_version;
    
        var pathMapIndex = -1;
        var pathMapCount = pathMapEnvelopes.length;
    
        while (++pathMapIndex < pathMapCount) {
    
            var pathMapEnvelope = pathMapEnvelopes[pathMapIndex];
    
            invalidatePathMap(pathMapEnvelope.json, cache, parent, node, version, expired, lru);
        }
    
        var newVersion = cache.$_version;
        var rootChangeHandler = modelRoot.onChange;
    
        if (isFunction(rootChangeHandler) && initialVersion !== newVersion) {
            rootChangeHandler();
        }
    };
    
    function invalidatePathMap(pathMap, root, parent, node, version, expired, lru) {
    
        if (isPrimitive(pathMap) || pathMap.$type) {
            return;
        }
    
        for (var key in pathMap) {
            if (key[0] !== __prefix && hasOwn(pathMap, key)) {
                var child = pathMap[key];
                var branch = isObject(child) && !child.$type;
                var results = invalidateNode(root, parent, node, key, branch, expired, lru);
                var nextNode = results[0];
                var nextParent = results[1];
                if (nextNode) {
                    if (branch) {
                        invalidatePathMap(child, root, nextParent, nextNode, version, expired, lru);
                    } else if (removeNodeAndDescendants(nextNode, nextParent, key, lru)) {
                        updateNodeAncestors(nextParent, getSize(nextNode), lru, version);
                    }
                }
            }
        }
    }
    
    function invalidateReference(root, node, expired, lru) {
    
        if (isExpired(node)) {
            expireNode(node, expired, lru);
            return [undefined, root];
        }
    
        promote(lru, node);
    
        var container = node;
        var reference = node.value;
        var parent = root;
    
        node = node.$_context;
    
        if (node != null) {
            parent = node.$_parent || root;
        } else {
    
            var index = 0;
            var count = reference.length - 1;
    
            parent = node = root;
    
            do {
                var key = reference[index];
                var branch = index < count;
                var results = invalidateNode(root, parent, node, key, branch, expired, lru);
                node = results[0];
                if (isPrimitive(node)) {
                    return results;
                }
                parent = results[1];
            } while (index++ < count);
    
            if (container.$_context !== node) {
                createHardlink(container, node);
            }
        }
    
        return [node, parent];
    }
    
    function invalidateNode(root, parent, node, key, branch, expired, lru) {
    
        var type = node.$type;
    
        while (type === $ref) {
            var results = invalidateReference(root, node, expired, lru);
    
            node = results[0];
    
            if (isPrimitive(node)) {
                return results;
            }
    
            parent = results[1];
            type = node && node.$type;
        }
    
        if (type !== void 0) {
            return [node, parent];
        }
    
        if (key == null) {
            if (branch) {
                throw new Error("`null` is not allowed in branch key positions.");
            } else if (node) {
                key = node.$_key;
            }
        } else {
            parent = node;
            node = parent[key];
        }
    
        return [node, parent];
    }
    
    ================================================ FILE: doc/invalidate_invalidatePathSets.js.html ================================================ --- layout: api-page title: "invalidate/invalidatePathSets.js" id: api ---

    invalidate/invalidatePathSets.js

    var __ref = require("./../internal/ref");
    
    var $ref = require("./../types/ref");
    
    var getBoundValue = require("./../get/getBoundValue");
    
    var promote = require("./../lru/promote");
    var getSize = require("./../support/getSize");
    var isExpired = require("./../support/isExpired");
    var isFunction = require("./../support/isFunction");
    var isPrimitive = require("./../support/isPrimitive");
    var expireNode = require("./../support/expireNode");
    var iterateKeySet = require("falcor-path-utils").iterateKeySet;
    var incrementVersion = require("./../support/incrementVersion");
    var updateNodeAncestors = require("./../support/updateNodeAncestors");
    var removeNodeAndDescendants = require("./../support/removeNodeAndDescendants");
    
    /**
     * Invalidates a list of Paths in a JSON Graph.
     * @function
     * @param {Object} model - the Model for which to insert the PathValues.
     * @param {Array.<PathValue>} paths - the PathValues to set.
     */
    
    module.exports = function invalidatePathSets(model, paths) {
    
        var modelRoot = model._root;
        var lru = modelRoot;
        var expired = modelRoot.expired;
        var version = incrementVersion();
        var bound = model._path;
        var cache = modelRoot.cache;
        var node = bound.length ? getBoundValue(model, bound).value : cache;
        // eslint-disable-next-line camelcase
        var parent = node.$_parent || cache;
        // eslint-disable-next-line camelcase
        var initialVersion = cache.$_version;
    
        var pathIndex = -1;
        var pathCount = paths.length;
    
        while (++pathIndex < pathCount) {
    
            var path = paths[pathIndex];
    
            invalidatePathSet(path, 0, cache, parent, node, version, expired, lru);
        }
    
        // eslint-disable-next-line camelcase
        var newVersion = cache.$_version;
        var rootChangeHandler = modelRoot.onChange;
    
        if (isFunction(rootChangeHandler) && initialVersion !== newVersion) {
            rootChangeHandler();
        }
    };
    
    function invalidatePathSet(
        path, depth, root, parent, node,
        version, expired, lru) {
    
        var note = {};
        var branch = depth < path.length - 1;
        var keySet = path[depth];
        var key = iterateKeySet(keySet, note);
    
        do {
            var results = invalidateNode(root, parent, node, key, branch, expired, lru);
            var nextNode = results[0];
            var nextParent = results[1];
            if (nextNode) {
                if (branch) {
                    invalidatePathSet(
                        path, depth + 1,
                        root, nextParent, nextNode,
                        version, expired, lru
                    );
                } else if (removeNodeAndDescendants(nextNode, nextParent, key, lru, undefined)) {
                    updateNodeAncestors(nextParent, getSize(nextNode), lru, version);
                }
            }
            key = iterateKeySet(keySet, note);
        } while (!note.done);
    }
    
    function invalidateReference(root, node, expired, lru) {
    
        if (isExpired(node)) {
            expireNode(node, expired, lru);
            return [undefined, root];
        }
    
        promote(lru, node);
    
        var container = node;
        var reference = node.value;
        var parent = root;
    
        // eslint-disable-next-line camelcase
        node = node.$_context;
    
        if (node != null) {
            // eslint-disable-next-line camelcase
            parent = node.$_parent || root;
        } else {
    
            var index = 0;
            var count = reference.length - 1;
    
            parent = node = root;
    
            do {
                var key = reference[index];
                var branch = index < count;
                var results = invalidateNode(root, parent, node, key, branch, expired, lru);
                node = results[0];
                if (isPrimitive(node)) {
                    return results;
                }
                parent = results[1];
            } while (index++ < count);
    
            // eslint-disable-next-line camelcase
            if (container.$_context !== node) {
                // eslint-disable-next-line camelcase
                var backRefs = node.$_refsLength || 0;
                // eslint-disable-next-line camelcase
                node.$_refsLength = backRefs + 1;
                node[__ref + backRefs] = container;
                // eslint-disable-next-line camelcase
                container.$_context = node;
                // eslint-disable-next-line camelcase
                container.$_refIndex = backRefs;
            }
        }
    
        return [node, parent];
    }
    
    function invalidateNode(root, parent, node, key, branch, expired, lru) {
    
        var type = node.$type;
    
        while (type === $ref) {
            var results = invalidateReference(root, node, expired, lru);
    
            node = results[0];
    
            if (isPrimitive(node)) {
                return results;
            }
    
            parent = results[1];
            type = node.$type;
        }
    
        if (type !== void 0) {
            return [node, parent];
        }
    
        if (key == null) {
            if (branch) {
                throw new Error("`null` is not allowed in branch key positions.");
            } else if (node) {
                key = node.$_key;
            }
        } else {
            parent = node;
            node = parent[key];
        }
    
        return [node, parent];
    }
    
    ================================================ FILE: doc/request_GetRequestV2.js.html ================================================ --- layout: api-page title: "request/GetRequestV2.js" id: api ---

    request/GetRequestV2.js

    var complement = require("./complement");
    var flushGetRequest = require("./flushGetRequest");
    var incrementVersion = require("../support/incrementVersion");
    var currentCacheVersion = require("../support/currentCacheVersion");
    
    var REQUEST_ID = 0;
    var GetRequestType = require("./RequestTypes").GetRequest;
    var setJSONGraphs = require("./../set/setJSONGraphs");
    var setPathValues = require("./../set/setPathValues");
    var $error = require("./../types/error");
    var emptyArray = [];
    var InvalidSourceError = require("./../errors/InvalidSourceError");
    
    /**
     * Creates a new GetRequest.  This GetRequest takes a scheduler and
     * the request queue.  Once the scheduler fires, all batched requests
     * will be sent to the server.  Upon request completion, the data is
     * merged back into the cache and all callbacks are notified.
     *
     * @param {Scheduler} scheduler -
     * @param {RequestQueueV2} requestQueue -
     * @param {number} attemptCount
     */
    var GetRequestV2 = function(scheduler, requestQueue, attemptCount) {
        this.sent = false;
        this.scheduled = false;
        this.requestQueue = requestQueue;
        this.id = ++REQUEST_ID;
        this.type = GetRequestType;
    
        this._scheduler = scheduler;
        this._attemptCount = attemptCount;
        this._pathMap = {};
        this._optimizedPaths = [];
        this._requestedPaths = [];
        this._callbacks = [];
        this._count = 0;
        this._disposable = null;
        this._collapsed = null;
        this._disposed = false;
    };
    
    GetRequestV2.prototype = {
        /**
         * batches the paths that are passed in.  Once the request is complete,
         * all callbacks will be called and the request will be removed from
         * parent queue.
         * @param {Array} requestedPaths -
         * @param {Array} optimizedPaths -
         * @param {Function} callback -
         */
        batch: function(requestedPaths, optimizedPaths, callback) {
            var self = this;
            var batchedOptPathSets = self._optimizedPaths;
            var batchedReqPathSets = self._requestedPaths;
            var batchedCallbacks = self._callbacks;
            var batchIx = batchedOptPathSets.length;
    
            // If its not sent, simply add it to the requested paths
            // and callbacks.
            batchedOptPathSets[batchIx] = optimizedPaths;
            batchedReqPathSets[batchIx] = requestedPaths;
            batchedCallbacks[batchIx] = callback;
            ++self._count;
    
            // If it has not been scheduled, then schedule the action
            if (!self.scheduled) {
                self.scheduled = true;
    
                var flushedDisposable;
                var scheduleDisposable = self._scheduler.schedule(function() {
                    flushedDisposable = flushGetRequest(self, batchedOptPathSets, function(err, data) {
                        var i, fn, len;
                        var model = self.requestQueue.model;
                        self.requestQueue.removeRequest(self);
                        self._disposed = true;
    
                        if (model._treatDataSourceErrorsAsJSONGraphErrors ? err instanceof InvalidSourceError : !!err) {
                            for (i = 0, len = batchedCallbacks.length; i < len; ++i) {
                                fn = batchedCallbacks[i];
                                if (fn) {
                                    fn(err);
                                }
                            }
                            return;
                        }
    
                        // If there is at least one callback remaining, then
                        // callback the callbacks.
                        if (self._count) {
                            // currentVersion will get added to each inserted
                            // node as node.$_version inside of self._merge.
                            //
                            // atom values just downloaded with $expires: 0
                            // (now-expired) will get assigned $_version equal
                            // to currentVersion, and checkCacheAndReport will
                            // later consider those nodes to not have expired
                            // for the duration of current event loop tick
                            //
                            // we unset currentCacheVersion after all callbacks
                            // have been called, to ensure that only these
                            // particular callbacks and any synchronous model.get
                            // callbacks inside of these, get the now-expired
                            // values
                            var currentVersion = incrementVersion.getCurrentVersion();
                            currentCacheVersion.setVersion(currentVersion);
                            var mergeContext = { hasInvalidatedResult: false };
    
                            var pathsErr = model._useServerPaths && data && data.paths === undefined ?
                                new Error("Server responses must include a 'paths' field when Model._useServerPaths === true") : undefined;
    
                            if (!pathsErr) {
                                self._merge(batchedReqPathSets, err, data, mergeContext);
                            }
    
                            // Call the callbacks.  The first one inserts all
                            // the data so that the rest do not have consider
                            // if their data is present or not.
                            for (i = 0, len = batchedCallbacks.length; i < len; ++i) {
                                fn = batchedCallbacks[i];
                                if (fn) {
                                    fn(pathsErr || err, data, mergeContext.hasInvalidatedResult);
                                }
                            }
                            currentCacheVersion.setVersion(null);
                        }
                    });
                    self._disposable = flushedDisposable;
                });
    
                // If the scheduler is sync then `flushedDisposable` will be
                // defined, and we want to use it, because that's what aborts an
                // in-flight XHR request, for example.
                // But if the scheduler is async, then `flushedDisposable` won't be
                // defined yet, and so we must use the scheduler's disposable until
                // `flushedDisposable` is defined. Since we want to still use
                // `flushedDisposable` once it is defined (to be able to abort in-
                // flight XHR requests), hence the reassignment of `_disposable`
                // above.
                self._disposable = flushedDisposable || scheduleDisposable;
            }
    
            // Disposes this batched request.  This does not mean that the
            // entire request has been disposed, but just the local one, if all
            // requests are disposed, then the outer disposable will be removed.
            return createDisposable(self, batchIx);
        },
    
        /**
         * Attempts to add paths to the outgoing request.  If there are added
         * paths then the request callback will be added to the callback list.
         * Handles adding partial paths as well
         *
         * @returns {Array} - whether new requested paths were inserted in this
         *                    request, the remaining paths that could not be added,
         *                    and disposable for the inserted requested paths.
         */
        add: function(requested, optimized, callback) {
            // uses the length tree complement calculator.
            var self = this;
            var complementResult = complement(requested, optimized, self._pathMap);
    
            var inserted = false;
            var disposable = false;
    
            // If we found an intersection, then just add new callback
            // as one of the dependents of that request
            if (complementResult.intersection.length) {
                inserted = true;
                var batchIx = self._callbacks.length;
                self._callbacks[batchIx] = callback;
                self._requestedPaths[batchIx] = complementResult.intersection;
                self._optimizedPaths[batchIx] = [];
                ++self._count;
    
                disposable = createDisposable(self, batchIx);
            }
    
            return [inserted, complementResult.requestedComplement, complementResult.optimizedComplement, disposable];
        },
    
        /**
         * merges the response into the model"s cache.
         */
        _merge: function(requested, err, data, mergeContext) {
            var self = this;
            var model = self.requestQueue.model;
            var modelRoot = model._root;
            var errorSelector = modelRoot.errorSelector;
            var comparator = modelRoot.comparator;
            var boundPath = model._path;
    
            model._path = emptyArray;
    
            // flatten all the requested paths, adds them to the
            var nextPaths = model._useServerPaths ? data.paths : flattenRequestedPaths(requested);
    
            // Insert errors in every requested position.
            if (err && model._treatDataSourceErrorsAsJSONGraphErrors) {
                var error = err;
    
                // Converts errors to objects, a more friendly storage
                // of errors.
                if (error instanceof Error) {
                    error = {
                        message: error.message
                    };
                }
    
                // Not all errors are value $types.
                if (!error.$type) {
                    error = {
                        $type: $error,
                        value: error
                    };
                }
    
                var pathValues = nextPaths.map(function(x) {
                    return {
                        path: x,
                        value: error
                    };
                });
                setPathValues(model, pathValues, null, errorSelector, comparator, mergeContext);
            }
    
            // Insert the jsonGraph from the dataSource.
            else {
                setJSONGraphs(model, [{
                    paths: nextPaths,
                    jsonGraph: data.jsonGraph
                }], null, errorSelector, comparator, mergeContext);
            }
    
            // return the model"s boundPath
            model._path = boundPath;
        }
    };
    
    // Creates a more efficient closure of the things that are
    // needed.  So the request and the batch index.  Also prevents code
    // duplication.
    function createDisposable(request, batchIx) {
        var disposed = false;
        return function() {
            if (disposed || request._disposed) {
                return;
            }
    
            disposed = true;
            request._callbacks[batchIx] = null;
            request._optimizedPaths[batchIx] = [];
            request._requestedPaths[batchIx] = [];
    
            // If there are no more requests, then dispose all of the request.
            var count = --request._count;
            var disposable = request._disposable;
            if (count === 0) {
                // looking for unsubscribe here to support more data sources (Rx)
                if (disposable.unsubscribe) {
                    disposable.unsubscribe();
                } else {
                    disposable.dispose();
                }
                request.requestQueue.removeRequest(request);
            }
        };
    }
    
    function flattenRequestedPaths(requested) {
        var out = [];
        var outLen = -1;
        for (var i = 0, len = requested.length; i < len; ++i) {
            var paths = requested[i];
            for (var j = 0, innerLen = paths.length; j < innerLen; ++j) {
                out[++outLen] = paths[j];
            }
        }
        return out;
    }
    
    module.exports = GetRequestV2;
    
    ================================================ FILE: doc/request_RequestQueueV2.js.html ================================================ --- layout: api-page title: "request/RequestQueueV2.js" id: api ---

    request/RequestQueueV2.js

    var RequestTypes = require("./RequestTypes");
    var sendSetRequest = require("./sendSetRequest");
    var GetRequest = require("./GetRequestV2");
    var falcorPathUtils = require("falcor-path-utils");
    
    /**
     * The request queue is responsible for queuing the operations to
     * the model"s dataSource.
     *
     * @param {Model} model -
     * @param {Scheduler} scheduler -
     */
    function RequestQueueV2(model, scheduler) {
        this.model = model;
        this.scheduler = scheduler;
        this.requests = this._requests = [];
    }
    
    RequestQueueV2.prototype = {
        /**
         * Sets the scheduler, but will not affect any current requests.
         */
        setScheduler: function(scheduler) {
            this.scheduler = scheduler;
        },
    
        /**
         * performs a set against the dataSource.  Sets, though are not batched
         * currently could be batched potentially in the future.  Since no batching
         * is required the setRequest action is simplified significantly.
         *
         * @param {JSONGraphEnvelope} jsonGraph -
         * @param {number} attemptCount
         * @param {Function} cb
         */
        set: function(jsonGraph, attemptCount, cb) {
            if (this.model._enablePathCollapse) {
                jsonGraph.paths = falcorPathUtils.collapse(jsonGraph.paths);
            }
    
            if (cb === undefined) {
                cb = attemptCount;
                attemptCount = undefined;
            }
    
            return sendSetRequest(jsonGraph, this.model, attemptCount, cb);
        },
    
        /**
         * Creates a get request to the dataSource.  Depending on the current
         * scheduler is how the getRequest will be flushed.
         * @param {Array} requestedPaths -
         * @param {Array} optimizedPaths -
         * @param {number} attemptCount
         * @param {Function} cb -
         */
        get: function(requestedPaths, optimizedPaths, attemptCount, cb) {
            var self = this;
            var disposables = [];
            var count = 0;
            var requests = self._requests;
            var i, len;
            var oRemainingPaths = optimizedPaths;
            var rRemainingPaths = requestedPaths;
            var disposed = false;
            var request;
    
            if (cb === undefined) {
                cb = attemptCount;
                attemptCount = undefined;
            }
    
            for (i = 0, len = requests.length; i < len; ++i) {
                request = requests[i];
                if (request.type !== RequestTypes.GetRequest) {
                    continue;
                }
    
                // The request has been sent, attempt to jump on the request
                // if possible.
                if (request.sent) {
                    if (this.model._enableRequestDeduplication) {
                        var results = request.add(rRemainingPaths, oRemainingPaths, refCountCallback);
    
                        // Checks to see if the results were successfully inserted
                        // into the outgoing results.  Then our paths will be reduced
                        // to the complement.
                        if (results[0]) {
                            rRemainingPaths = results[1];
                            oRemainingPaths = results[2];
                            disposables[disposables.length] = results[3];
                            ++count;
    
                            // If there are no more remaining paths then exit the loop.
                            if (!oRemainingPaths.length) {
                                break;
                            }
                        }
                    }
                }
    
                // If there is an unsent request, then we can batch and leave.
                else {
                    request.batch(rRemainingPaths, oRemainingPaths, refCountCallback);
                    oRemainingPaths = null;
                    rRemainingPaths = null;
                    ++count;
                    break;
                }
            }
    
            // After going through all the available requests if there are more
            // paths to process then a new request must be made.
            if (oRemainingPaths && oRemainingPaths.length) {
                request = new GetRequest(self.scheduler, self, attemptCount);
                requests[requests.length] = request;
                ++count;
                var disposable = request.batch(rRemainingPaths, oRemainingPaths, refCountCallback);
                disposables[disposables.length] = disposable;
            }
    
            // This is a simple refCount callback.
            function refCountCallback(err, data, hasInvalidatedResult) {
                if (disposed) {
                    return;
                }
    
                --count;
    
                // If the count becomes 0, then its time to notify the
                // listener that the request is done.
                if (count === 0) {
                    cb(err, data, hasInvalidatedResult);
                }
            }
    
            // When disposing the request all of the outbound requests will be
            // disposed of.
            return function() {
                if (disposed || count === 0) {
                    return;
                }
    
                disposed = true;
                var length = disposables.length;
                for (var idx = 0; idx < length; ++idx) {
                    disposables[idx]();
                }
            };
        },
    
        /**
         * Removes the request from the request queue.
         */
        removeRequest: function(request) {
            var requests = this._requests;
            var i = requests.length;
            while (--i >= 0) {
                if (requests[i].id === request.id) {
                    requests.splice(i, 1);
                    break;
                }
            }
        }
    };
    
    module.exports = RequestQueueV2;
    
    ================================================ FILE: doc/request_complement.js.html ================================================ --- layout: api-page title: "request/complement.js" id: api ---

    request/complement.js

    var iterateKeySet = require("falcor-path-utils").iterateKeySet;
    
    /**
     * Calculates what paths in requested path sets can be deduplicated based on an existing optimized path tree.
     *
     * For path sets with ranges or key sets, if some expanded paths can be found in the path tree, only matching paths are
     * returned as intersection. The non-matching expanded paths are returned as complement.
     *
     * The function returns an object consisting of:
     * - intersection: requested paths that were matched to the path tree
     * - optimizedComplement: optimized paths that were not found in the path tree
     * - requestedComplement: requested paths for the optimized paths that were not found in the path tree
     */
    module.exports = function complement(requested, optimized, tree) {
        var optimizedComplement = [];
        var requestedComplement = [];
        var intersection = [];
        var i, iLen;
    
        for (i = 0, iLen = optimized.length; i < iLen; ++i) {
            var oPath = optimized[i];
            var rPath = requested[i];
            var subTree = tree[oPath.length];
    
            var intersectionData = findPartialIntersections(rPath, oPath, subTree);
            Array.prototype.push.apply(intersection, intersectionData[0]);
            Array.prototype.push.apply(optimizedComplement, intersectionData[1]);
            Array.prototype.push.apply(requestedComplement, intersectionData[2]);
        }
    
        return {
            intersection: intersection,
            optimizedComplement: optimizedComplement,
            requestedComplement: requestedComplement
        };
    };
    
    /**
     * Recursive function to calculate intersection and complement paths in 2 given pathsets at a given depth.
     *
     * Parameters:
     *  - requestedPath: full requested path set (can include ranges)
     *  - optimizedPath: corresponding optimized path (can include ranges)
     *  - requestTree: path tree for in-flight request, against which to dedupe
     *
     * Returns a 3-tuple consisting of
     *  - the intersection of requested paths with requestTree
     *  - the complement of optimized paths with requestTree
     *  - the complement of corresponding requested paths with requestTree
     *
     * Example scenario:
     *  - requestedPath: ['lolomo', 0, 0, 'tags', { from: 0, to: 2 }]
     *  - optimizedPath: ['videosById', 11, 'tags', { from: 0, to: 2 }]
     *  - requestTree: { videosById: 11: { tags: { 0: null, 1: null }}}
     *
     * This returns:
     * [
     *   [['lolomo', 0, 0, 'tags', 0], ['lolomo', 0, 0, 'tags', 1]],
     *   [['videosById', 11, 'tags', 2]],
     *   [['lolomo', 0, 0, 'tags', 2]]
     * ]
     *
     */
    function findPartialIntersections(requestedPath, optimizedPath, requestTree) {
        var depthDiff = requestedPath.length - optimizedPath.length;
        var i;
    
        // Descend into the request path tree for the optimized-path prefix (when the optimized path is longer than the
        // requested path)
        for (i = 0; requestTree && i < -depthDiff; i++) {
            requestTree = requestTree[optimizedPath[i]];
        }
    
        // There is no matching path in the request path tree, thus no candidates for deduplication
        if (!requestTree) {
            return [[], [optimizedPath], [requestedPath]];
        }
    
        if (depthDiff === 0) {
            return recurse(requestedPath, optimizedPath, requestTree, 0, [], []);
        } else if (depthDiff > 0) {
            return recurse(requestedPath, optimizedPath, requestTree, 0, requestedPath.slice(0, depthDiff), []);
        } else {
            return recurse(requestedPath, optimizedPath, requestTree, -depthDiff, [], optimizedPath.slice(0, -depthDiff));
        }
    }
    
    function recurse(requestedPath, optimizedPath, currentTree, depth, rCurrentPath, oCurrentPath) {
        var depthDiff = requestedPath.length - optimizedPath.length;
        var intersections = [];
        var rComplementPaths = [];
        var oComplementPaths = [];
        var oPathLen = optimizedPath.length;
    
        // Loop over the optimized path, looking for deduplication opportunities
        for (; depth < oPathLen; ++depth) {
            var key = optimizedPath[depth];
            var keyType = typeof key;
    
            if (key && keyType === "object") {
                // If a range key is found, start an inner loop to iterate over all keys in the range, and add
                // intersections and complements from each iteration separately.
                //
                // Range keys branch out this way, providing individual deduping opportunities for each inner key.
                var note = {};
                var innerKey = iterateKeySet(key, note);
    
                while (!note.done) {
                    var nextTree = currentTree[innerKey];
                    if (nextTree === undefined) {
                        // If no next sub tree exists for an inner key, it's a dead-end and we can add this to
                        // complement paths
                        oComplementPaths[oComplementPaths.length] = arrayConcatSlice2(
                            oCurrentPath,
                            innerKey,
                            optimizedPath, depth + 1
                        );
                        rComplementPaths[rComplementPaths.length] = arrayConcatSlice2(
                            rCurrentPath,
                            innerKey,
                            requestedPath, depth + 1 + depthDiff
                        );
                    } else if (depth === oPathLen - 1) {
                        // Reaching the end of the optimized path means that we found the entire path in the path tree,
                        // so add it to intersections
                        intersections[intersections.length] = arrayConcatElement(rCurrentPath, innerKey);
                    } else {
                        // Otherwise keep trying to find further partial deduping opportunities in the remaining path
                        var intersectionData = recurse(
                            requestedPath, optimizedPath,
                            nextTree,
                            depth + 1,
                            arrayConcatElement(rCurrentPath, innerKey),
                            arrayConcatElement(oCurrentPath, innerKey)
                        );
    
                        Array.prototype.push.apply(intersections, intersectionData[0]);
                        Array.prototype.push.apply(oComplementPaths, intersectionData[1]);
                        Array.prototype.push.apply(rComplementPaths, intersectionData[2]);
                    }
                    innerKey = iterateKeySet(key, note);
                }
    
                // The remainder of the path was handled by the recursive call, terminate the loop
                break;
            } else {
                // For simple keys, we don't need to branch out. Loop over `depth` instead of iterating over a range.
                currentTree = currentTree[key];
                oCurrentPath[oCurrentPath.length] = optimizedPath[depth];
                rCurrentPath[rCurrentPath.length] = requestedPath[depth + depthDiff];
    
                if (currentTree === undefined) {
                    // The path was not found in the tree, add this to complements
                    oComplementPaths[oComplementPaths.length] = arrayConcatSlice(
                        oCurrentPath, optimizedPath, depth + 1
                    );
                    rComplementPaths[rComplementPaths.length] = arrayConcatSlice(
                        rCurrentPath, requestedPath, depth + depthDiff + 1
                    );
    
                    break;
                } else if (depth === oPathLen - 1) {
                    // The end of optimized path was reached, add to intersections
                    intersections[intersections.length] = rCurrentPath;
                }
            }
        }
    
        // Return accumulated intersection and complement paths
        return [intersections, oComplementPaths, rComplementPaths];
    }
    
    // Exported for unit testing.
    module.exports.__test = { findPartialIntersections: findPartialIntersections };
    
    /**
     * Create a new array consisting of a1 + a subset of a2. Avoids allocating an extra array by calling `slice` on a2.
     */
    function arrayConcatSlice(a1, a2, start) {
        var result = a1.slice();
        var l1 = result.length;
        var length = a2.length - start;
        result.length = l1 + length;
        for (var i = 0; i < length; ++i) {
            result[l1 + i] = a2[start + i];
        }
        return result;
    }
    
    /**
     * Create a new array consisting of a1 + a2 + a subset of a3. Avoids allocating an extra array by calling `slice` on a3.
     */
    function arrayConcatSlice2(a1, a2, a3, start) {
        var result = a1.concat(a2);
        var l1 = result.length;
        var length = a3.length - start;
        result.length = l1 + length;
        for (var i = 0; i < length; ++i) {
            result[l1 + i] = a3[start + i];
        }
        return result;
    }
    
    /**
     * Create a new array consistent of a1 plus an additional element. Avoids the unnecessary array allocation when using `a1.concat([element])`.
     */
    function arrayConcatElement(a1, element) {
        var result = a1.slice();
        result.push(element);
        return result;
    }
    
    ================================================ FILE: doc/response_AssignableDisposable.js.html ================================================ --- layout: api-page title: "response/AssignableDisposable.js" id: api ---

    response/AssignableDisposable.js

    /**
     * Will allow for state tracking of the current disposable.  Also fulfills the
     * disposable interface.
     * @private
     */
    var AssignableDisposable = function AssignableDisposable(disosableCallback) {
        this.disposed = false;
        this.currentDisposable = disosableCallback;
    };
    
    
    AssignableDisposable.prototype = {
    
        /**
         * Disposes of the current disposable.  This would be the getRequestCycle
         * disposable.
         */
        dispose: function dispose() {
            if (this.disposed || !this.currentDisposable) {
                return;
            }
            this.disposed = true;
    
            // If the current disposable fulfills the disposable interface or just
            // a disposable function.
            var currentDisposable = this.currentDisposable;
            if (currentDisposable.dispose) {
                currentDisposable.dispose();
            }
    
            else {
                currentDisposable();
            }
        }
    };
    
    
    module.exports = AssignableDisposable;
    
    ================================================ FILE: doc/response_ModelResponse.js.html ================================================ --- layout: api-page title: "response/ModelResponse.js" id: api ---

    response/ModelResponse.js

    var ModelResponseObserver = require("./ModelResponseObserver");
    var $$observable = require("symbol-observable").default;
    var toEsObservable = require("../toEsObservable");
    
    /**
     * A ModelResponse is a container for the results of a get, set, or call operation performed on a Model. The ModelResponse provides methods which can be used to specify the output format of the data retrieved from a Model, as well as how that data is delivered.
     * @constructor ModelResponse
     * @augments Observable
    */
    function ModelResponse(subscribe) {
        this._subscribe = subscribe;
    }
    
    ModelResponse.prototype[$$observable] = function SymbolObservable() {
        return toEsObservable(this);
    };
    
    ModelResponse.prototype._toJSONG = function toJSONG() {
        return this;
    };
    
    /**
     * The progressively method breaks the response up into two parts: the data immediately available in the Model cache, and the data in the Model cache after the missing data has been retrieved from the DataSource.
     * The progressively method creates a ModelResponse that immediately returns the requested data that is available in the Model cache. If any requested paths are not available in the cache, the ModelResponse will send another JSON message with all of the requested data after it has been retrieved from the DataSource.
     * @name progressively
     * @memberof ModelResponse.prototype
     * @function
     * @return {ModelResponse.<JSONEnvelope>} the values found at the requested paths.
     * @example
    var dataSource = (new falcor.Model({
      cache: {
        user: {
          name: "Steve",
          surname: "McGuire",
          age: 31
        }
      }
    })).asDataSource();
    
    var model = new falcor.Model({
      source: dataSource,
      cache: {
        user: {
          name: "Steve",
          surname: "McGuire"
        }
      }
    });
    
    model.
      get(["user",["name", "surname", "age"]]).
      progressively().
      // this callback will be invoked twice, once with the data in the
      // Model cache, and again with the additional data retrieved from the DataSource.
      subscribe(function(json){
        console.log(JSON.stringify(json,null,4));
      });
    
    // prints...
    // {
    //     "json": {
    //         "user": {
    //             "name": "Steve",
    //             "surname": "McGuire"
    //         }
    //     }
    // }
    // ...and then prints...
    // {
    //     "json": {
    //         "user": {
    //             "name": "Steve",
    //             "surname": "McGuire",
    //             "age": 31
    //         }
    //     }
    // }
    */
    ModelResponse.prototype.progressively = function progressively() {
        return this;
    };
    
    ModelResponse.prototype.subscribe =
    ModelResponse.prototype.forEach = function subscribe(a, b, c) {
        var observer = new ModelResponseObserver(a, b, c);
        var subscription = this._subscribe(observer);
        switch (typeof subscription) {
            case "function":
                return {
                    dispose: function() {
                        if (observer._closed) {
                            return;
                        }
                        observer._closed = true;
                        subscription();
                    }
                 };
            case "object":
                return {
                    dispose: function() {
                        if (observer._closed) {
                            return;
                        }
                        observer._closed = true;
                        if (subscription !== null) {
                            subscription.dispose();
                        }
                    }
                 };
            default:
                return {
                    dispose: function() {
                        observer._closed = true;
                    }
                 };
        }
    };
    
    ModelResponse.prototype.then = function then(onNext, onError) {
        /* global Promise */
        var self = this;
        if (!self._promise) {
            self._promise = new Promise(function(resolve, reject) {
                var rejected = false;
                var values = [];
                self.subscribe(
                    function(value) {
                        values[values.length] = value;
                    },
                    function(errors) {
                        rejected = true;
                        reject(errors);
                    },
                    function() {
                        var value = values;
                        if (values.length <= 1) {
                            value = values[0];
                        }
    
                        if (rejected === false) {
                            resolve(value);
                        }
                    }
                );
            });
        }
        return self._promise.then(onNext, onError);
    };
    
    module.exports = ModelResponse;
    
    ================================================ FILE: doc/response_ModelResponseObserver.js.html ================================================ --- layout: api-page title: "response/ModelResponseObserver.js" id: api ---

    response/ModelResponseObserver.js

    var noop = require("./../support/noop");
    
    /**
     * A ModelResponseObserver conform to the Observable's Observer contract. It accepts either an Observer or three optional callbacks which correspond to the Observer methods onNext, onError, and onCompleted.
     * The ModelResponseObserver wraps an Observer to enforce a variety of different invariants including:
     * 1. onError callback is only called once.
     * 2. onCompleted callback is only called once.
     * @constructor ModelResponseObserver
    */
    function ModelResponseObserver(
        onNextOrObserver,
        onErrorFn,
        onCompletedFn
    ) {
        // if callbacks are passed, construct an Observer from them. Create a NOOP function for any missing callbacks.
        if (!onNextOrObserver || typeof onNextOrObserver !== "object") {
            this._observer = {
                onNext: (
                    typeof onNextOrObserver === "function"
                        ? onNextOrObserver
                        : noop
                ),
                onError: (
                    typeof onErrorFn === "function"
                        ? onErrorFn
                        : noop
                ),
                onCompleted: (
                    typeof onCompletedFn === "function"
                        ? onCompletedFn
                        : noop
                )
            };
        }
        // if an Observer is passed
        else {
            this._observer = {
                onNext: typeof onNextOrObserver.onNext === "function" ? function(value) { onNextOrObserver.onNext(value); } : noop,
                onError: typeof onNextOrObserver.onError === "function" ? function(error) { onNextOrObserver.onError(error); } : noop,
                onCompleted: (
                    typeof onNextOrObserver.onCompleted === "function"
                        ? function() { onNextOrObserver.onCompleted(); }
                        : noop
                )
            };
        }
    }
    
    ModelResponseObserver.prototype = {
        onNext: function(v) {
            if (!this._closed) {
                this._observer.onNext(v);
            }
        },
        onError: function(e) {
            if (!this._closed) {
                this._closed = true;
                this._observer.onError(e);
            }
        },
        onCompleted: function() {
            if (!this._closed) {
                this._closed = true;
                this._observer.onCompleted();
            }
        }
    };
    
    module.exports = ModelResponseObserver;
    
    ================================================ FILE: doc/response_get_GetResponse.js.html ================================================ --- layout: api-page title: "response/get/GetResponse.js" id: api ---

    response/get/GetResponse.js

    var ModelResponse = require("./../ModelResponse");
    var checkCacheAndReport = require("./checkCacheAndReport");
    var getRequestCycle = require("./getRequestCycle");
    var empty = {dispose: function() {}};
    var collectLru = require("./../../lru/collect");
    var getSize = require("./../../support/getSize");
    
    /**
     * The get response.  It takes in a model and paths and starts
     * the request cycle.  It has been optimized for cache first requests
     * and closures.
     * @param {Model} model -
     * @param {Array} paths -
     * @augments ModelResponse
     * @private
     */
    var GetResponse = function GetResponse(model, paths, isJSONGraph,
                                           isProgressive, forceCollect) {
        this.model = model;
        this.currentRemainingPaths = paths;
        this.isJSONGraph = isJSONGraph || false;
        this.isProgressive = isProgressive || false;
        this.forceCollect = forceCollect || false;
    };
    
    GetResponse.prototype = Object.create(ModelResponse.prototype);
    
    /**
     * Makes the output of a get response JSONGraph instead of json.
     * @private
     */
    GetResponse.prototype._toJSONG = function _toJSONGraph() {
        return new GetResponse(this.model, this.currentRemainingPaths,
                               true, this.isProgressive, this.forceCollect);
    };
    
    /**
     * Progressively responding to data in the cache instead of once the whole
     * operation is complete.
     * @public
     */
    GetResponse.prototype.progressively = function progressively() {
        return new GetResponse(this.model, this.currentRemainingPaths,
                               this.isJSONGraph, true, this.forceCollect);
    };
    
    /**
     * purely for the purposes of closure creation other than the initial
     * prototype created closure.
     *
     * @private
     */
    GetResponse.prototype._subscribe = function _subscribe(observer) {
        var seed = [{}];
        var errors = [];
        var model = this.model;
        var isJSONG = observer.isJSONG = this.isJSONGraph;
        var isProgressive = this.isProgressive;
        var results = checkCacheAndReport(model, this.currentRemainingPaths,
                                          observer, isProgressive, isJSONG, seed,
                                          errors);
    
        // If there are no results, finish.
        if (!results) {
            if (this.forceCollect) {
                var modelRoot = model._root;
                var modelCache = modelRoot.cache;
                var currentVersion = modelCache.$_version;
    
                collectLru(modelRoot, modelRoot.expired, getSize(modelCache),
                        model._maxSize, model._collectRatio, currentVersion);
            }
            return empty;
        }
    
        // Starts the async request cycle.
        return getRequestCycle(this, model, results,
                               observer, errors, 1);
    };
    
    module.exports = GetResponse;
    
    ================================================ FILE: doc/response_set_SetResponse.js.html ================================================ --- layout: api-page title: "response/set/SetResponse.js" id: api ---

    response/set/SetResponse.js

    var ModelResponse = require("./../ModelResponse");
    var pathSyntax = require("falcor-path-syntax");
    var isArray = Array.isArray;
    var isPathValue = require("./../../support/isPathValue");
    var isJSONGraphEnvelope = require("./../../support/isJSONGraphEnvelope");
    var isJSONEnvelope = require("./../../support/isJSONEnvelope");
    var setRequestCycle = require("./setRequestCycle");
    
    /**
     *  The set response is responsible for doing the request loop for the set
     * operation and subscribing to the follow up get.
     *
     * The constructors job is to parse out the arguments and put them in their
     * groups.  The following subscribe will do the actual cache set and dataSource
     * operation remoting.
     *
     * @param {Model} model -
     * @param {Array} args - The array of arguments that can be JSONGraph, JSON, or
     * pathValues.
     * @param {Boolean} isJSONGraph - if the request is a jsonGraph output format.
     * @param {Boolean} isProgressive - progressive output.
     * @augments ModelResponse
     * @private
     */
    var SetResponse = function SetResponse(model, args, isJSONGraph,
                                           isProgressive) {
    
        // The response properties.
        this._model = model;
        this._isJSONGraph = isJSONGraph || false;
        this._isProgressive = isProgressive || false;
        this._initialArgs = args;
        this._value = [{}];
    
        var groups = [];
        var group, groupType;
        var argIndex = -1;
        var argCount = args.length;
    
        // Validation of arguments have been moved out of this function.
        while (++argIndex < argCount) {
            var arg = args[argIndex];
            var argType;
            if (isArray(arg) || typeof arg === "string") {
                arg = pathSyntax.fromPath(arg);
                argType = "PathValues";
            } else if (isPathValue(arg)) {
                arg.path = pathSyntax.fromPath(arg.path);
                argType = "PathValues";
            } else if (isJSONGraphEnvelope(arg)) {
                argType = "JSONGs";
            } else if (isJSONEnvelope(arg)) {
                argType = "PathMaps";
            }
    
            if (groupType !== argType) {
                groupType = argType;
                group = {
                    inputType: argType,
                    arguments: []
                };
                groups.push(group);
            }
    
            group.arguments.push(arg);
        }
    
        this._groups = groups;
    };
    
    SetResponse.prototype = Object.create(ModelResponse.prototype);
    
    /**
     * The subscribe function will setup the remoting of the operation and cache
     * setting.
     *
     * @private
     */
    SetResponse.prototype._subscribe = function _subscribe(observer) {
        var groups = this._groups;
        var model = this._model;
        var isJSONGraph = this._isJSONGraph;
        var isProgressive = this._isProgressive;
    
        // Starts the async request cycle.
        return setRequestCycle(
            model, observer, groups, isJSONGraph, isProgressive, 1);
    };
    
    /**
     * Makes the output of a get response JSONGraph instead of json.
     * @private
     */
    SetResponse.prototype._toJSONG = function _toJSONGraph() {
        return new SetResponse(this._model, this._initialArgs,
                               true, this._isProgressive);
    };
    
    /**
     * Progressively responding to data in the cache instead of once the whole
     * operation is complete.
     * @public
     */
    SetResponse.prototype.progressively = function progressively() {
        return new SetResponse(this._model, this._initialArgs,
                               this._isJSONGraph, true);
    };
    
    module.exports = SetResponse;
    
    ================================================ FILE: doc/response_set_setGroupsIntoCache.js.html ================================================ --- layout: api-page title: "response/set/setGroupsIntoCache.js" id: api ---

    response/set/setGroupsIntoCache.js

    var arrayFlatMap = require("./../../support/array-flat-map");
    
    /**
     * Takes the groups that are created in the SetResponse constructor and sets
     * them into the cache.
     */
    module.exports = function setGroupsIntoCache(model, groups) {
        var modelRoot = model._root;
        var errorSelector = modelRoot.errorSelector;
        var groupIndex = -1;
        var groupCount = groups.length;
        var requestedPaths = [];
        var optimizedPaths = [];
        var returnValue = {
            requestedPaths: requestedPaths,
            optimizedPaths: optimizedPaths
        };
    
        // Takes each of the groups and normalizes their input into
        // requested paths and optimized paths.
        while (++groupIndex < groupCount) {
    
            var group = groups[groupIndex];
            var inputType = group.inputType;
            var methodArgs = group.arguments;
    
            if (methodArgs.length > 0) {
                var operationName = "_set" + inputType;
                var operationFunc = model[operationName];
                var successfulPaths = operationFunc(model, methodArgs, null, errorSelector);
    
                optimizedPaths.push.apply(optimizedPaths, successfulPaths[1]);
    
                if (inputType === "PathValues") {
                    requestedPaths.push.apply(requestedPaths, methodArgs.map(pluckPath));
                } else if (inputType === "JSONGs") {
                    requestedPaths.push.apply(requestedPaths, arrayFlatMap(methodArgs, pluckEnvelopePaths));
                } else {
                    requestedPaths.push.apply(requestedPaths, successfulPaths[0]);
                }
            }
        }
    
        return returnValue;
    };
    
    function pluckPath(pathValue) {
        return pathValue.path;
    }
    
    function pluckEnvelopePaths(jsonGraphEnvelope) {
        return jsonGraphEnvelope.paths;
    }
    
    ================================================ FILE: doc/response_set_setRequestCycle.js.html ================================================ --- layout: api-page title: "response/set/setRequestCycle.js" id: api ---

    response/set/setRequestCycle.js

    var emptyArray = [];
    var AssignableDisposable = require("./../AssignableDisposable");
    var GetResponse = require("./../get/GetResponse");
    var setGroupsIntoCache = require("./setGroupsIntoCache");
    var getWithPathsAsPathMap = require("./../../get").getWithPathsAsPathMap;
    var InvalidSourceError = require("./../../errors/InvalidSourceError");
    var MaxRetryExceededError = require("./../../errors/MaxRetryExceededError");
    
    /**
     * The request cycle for set.  This is responsible for requesting to dataSource
     * and allowing disposing inflight requests.
     */
    module.exports = function setRequestCycle(model, observer, groups,
                                              isJSONGraph, isProgressive, count) {
        var requestedAndOptimizedPaths = setGroupsIntoCache(model, groups);
        var optimizedPaths = requestedAndOptimizedPaths.optimizedPaths;
        var requestedPaths = requestedAndOptimizedPaths.requestedPaths;
    
        // we have exceeded the maximum retry limit.
        if (count > model._maxRetries) {
            observer.onError(new MaxRetryExceededError(optimizedPaths));
            return {
                dispose: function() {}
            };
        }
    
        var isMaster = model._source === undefined;
    
        // Local set only.  We perform a follow up get.  If performance is ever
        // a requirement simply requiring in checkCacheAndReport and use get request
        // internals.  Figured this is more "pure".
        if (isMaster) {
            return subscribeToFollowupGet(model, observer, requestedPaths,
                                  isJSONGraph, isProgressive);
        }
    
    
        // Progressively output the data from the first set.
        var prevVersion;
        if (isProgressive) {
            var results = getWithPathsAsPathMap(model, requestedPaths, [{}]);
            if (results.criticalError) {
                observer.onError(results.criticalError);
                return null;
            }
            observer.onNext(results.values[0]);
    
            prevVersion = model._root.cache.$_version;
        }
    
        var currentJSONGraph = getJSONGraph(model, optimizedPaths);
        var disposable = new AssignableDisposable();
    
        // Sends out the setRequest.  The Queue will call the callback with the
        // JSONGraph envelope / error.
        var requestDisposable = model._request.
            // TODO: There is error handling that has not been addressed yet.
    
            // If disposed before this point then the sendSetRequest will not
            // further any callbacks.  Therefore, if we are at this spot, we are
            // not disposed yet.
            set(currentJSONGraph, count, function(error, jsonGraphEnv) {
                if (error instanceof InvalidSourceError) {
                    observer.onError(error);
                    return;
                }
    
                // TODO: This seems like there are errors with this approach, but
                // for sanity sake I am going to keep this logic in here until a
                // rethink can be done.
                var isCompleted = false;
                if (error || optimizedPaths.length === jsonGraphEnv.paths.length) {
                    isCompleted = true;
                }
    
                // If we're in progressive mode and nothing changed in the meantime, we're done
                if (isProgressive) {
                    var nextVersion = model._root.cache.$_version;
                    var versionChanged = nextVersion !== prevVersion;
    
                    if (!versionChanged) {
                        observer.onCompleted();
                        return;
                    }
                }
    
                // Happy case.  One request to the dataSource will fulfill the
                // required paths.
                if (isCompleted) {
                    disposable.currentDisposable =
                        subscribeToFollowupGet(model, observer, requestedPaths,
                                              isJSONGraph, isProgressive);
                }
    
                // TODO: The unhappy case.  I am unsure how this can even be
                // achieved.
                else {
                    // We need to restart the setRequestCycle.
                    setRequestCycle(model, observer, groups, isJSONGraph,
                                    isProgressive, count + 1);
                }
            });
    
        // Sets the current disposable as the requestDisposable.
        disposable.currentDisposable = requestDisposable;
    
        return disposable;
    };
    
    function getJSONGraph(model, optimizedPaths) {
        var boundPath = model._path;
        var envelope = {};
        model._path = emptyArray;
        model._getPathValuesAsJSONG(model._materialize().withoutDataSource(), optimizedPaths, [envelope]);
        model._path = boundPath;
    
        return envelope;
    }
    
    function subscribeToFollowupGet(model, observer, requestedPaths, isJSONGraph,
                                   isProgressive) {
    
        // Creates a new response and subscribes to it with the original observer.
        // Also sets forceCollect to true, incase the operation is synchronous and
        // exceeds the cache limit size
        var response = new GetResponse(model, requestedPaths, isJSONGraph,
                                       isProgressive, true);
        return response.subscribe(observer);
    }
    
    ================================================ FILE: doc/set_setJSONGraphs.js.html ================================================ --- layout: api-page title: "set/setJSONGraphs.js" id: api ---

    set/setJSONGraphs.js

    var createHardlink = require("./../support/createHardlink");
    var $ref = require("./../types/ref");
    
    var isExpired = require("./../support/isAlreadyExpired");
    var isFunction = require("./../support/isFunction");
    var isPrimitive = require("./../support/isPrimitive");
    var expireNode = require("./../support/expireNode");
    var iterateKeySet = require("falcor-path-utils").iterateKeySet;
    var incrementVersion = require("./../support/incrementVersion");
    var mergeJSONGraphNode = require("./../support/mergeJSONGraphNode");
    var NullInPathError = require("./../errors/NullInPathError");
    
    /**
     * Merges a list of {@link JSONGraphEnvelope}s into a {@link JSONGraph}.
     * @function
     * @param {Object} model - the Model for which to merge the {@link JSONGraphEnvelope}s.
     * @param {Array.<PathValue>} jsonGraphEnvelopes - the {@link JSONGraphEnvelope}s to merge.
     * @return {Array.<Array.<Path>>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values.
     */
    
    module.exports = function setJSONGraphs(model, jsonGraphEnvelopes, x, errorSelector, comparator, replacedPaths) {
    
        var modelRoot = model._root;
        var lru = modelRoot;
        var expired = modelRoot.expired;
        var version = incrementVersion();
        var cache = modelRoot.cache;
        var initialVersion = cache.$_version;
    
        var requestedPath = [];
        var optimizedPath = [];
        var requestedPaths = [];
        var optimizedPaths = [];
        var jsonGraphEnvelopeIndex = -1;
        var jsonGraphEnvelopeCount = jsonGraphEnvelopes.length;
    
        while (++jsonGraphEnvelopeIndex < jsonGraphEnvelopeCount) {
    
            var jsonGraphEnvelope = jsonGraphEnvelopes[jsonGraphEnvelopeIndex];
            var paths = jsonGraphEnvelope.paths;
            var jsonGraph = jsonGraphEnvelope.jsonGraph;
    
            var pathIndex = -1;
            var pathCount = paths.length;
    
            while (++pathIndex < pathCount) {
    
                var path = paths[pathIndex];
                optimizedPath.index = 0;
    
                setJSONGraphPathSet(
                    path, 0,
                    cache, cache, cache,
                    jsonGraph, jsonGraph, jsonGraph,
                    requestedPaths, optimizedPaths, requestedPath, optimizedPath,
                    version, expired, lru, comparator, errorSelector, replacedPaths
                );
            }
        }
    
        var newVersion = cache.$_version;
        var rootChangeHandler = modelRoot.onChange;
    
        if (isFunction(rootChangeHandler) && initialVersion !== newVersion) {
            rootChangeHandler();
        }
    
        return [requestedPaths, optimizedPaths];
    };
    
    /* eslint-disable no-constant-condition */
    function setJSONGraphPathSet(
        path, depth, root, parent, node,
        messageRoot, messageParent, message,
        requestedPaths, optimizedPaths, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector, replacedPaths) {
    
        var note = {};
        var branch = depth < path.length - 1;
        var keySet = path[depth];
        var key = iterateKeySet(keySet, note);
        var optimizedIndex = optimizedPath.index;
    
        do {
    
            requestedPath.depth = depth;
    
            var results = setNode(
                root, parent, node, messageRoot, messageParent, message,
                key, branch, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector, replacedPaths
            );
    
            requestedPath[depth] = key;
            requestedPath.index = depth;
            optimizedPath[optimizedPath.index++] = key;
            var nextNode = results[0];
            var nextParent = results[1];
            if (nextNode) {
                if (branch) {
                    setJSONGraphPathSet(
                        path, depth + 1, root, nextParent, nextNode,
                        messageRoot, results[3], results[2],
                        requestedPaths, optimizedPaths, requestedPath, optimizedPath,
                        version, expired, lru, comparator, errorSelector, replacedPaths
                    );
                } else {
                    requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1));
                    optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index));
                }
            }
            key = iterateKeySet(keySet, note);
            if (note.done) {
                break;
            }
            optimizedPath.index = optimizedIndex;
        } while (true);
    }
    /* eslint-enable */
    
    var _result = new Array(4);
    function setReference(
        root, node, messageRoot, message, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector, replacedPaths) {
    
        var reference = node.value;
        optimizedPath.length = 0;
        optimizedPath.push.apply(optimizedPath, reference);
    
        if (isExpired(node)) {
            optimizedPath.index = reference.length;
            expireNode(node, expired, lru);
            _result[0] = undefined;
            _result[1] = root;
            _result[2] = message;
            _result[3] = messageRoot;
            return _result;
        }
    
        var index = 0;
        var container = node;
        var count = reference.length - 1;
        var parent = node = root;
        var messageParent = message = messageRoot;
    
        do {
            var key = reference[index];
            var branch = index < count;
            optimizedPath.index = index;
    
            var results = setNode(
                root, parent, node, messageRoot, messageParent, message,
                key, branch, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector, replacedPaths
            );
            node = results[0];
            if (isPrimitive(node)) {
                optimizedPath.index = index;
                return results;
            }
            parent = results[1];
            message = results[2];
            messageParent = results[3];
        } while (index++ < count);
    
        optimizedPath.index = index;
    
        if (container.$_context !== node) {
            createHardlink(container, node);
        }
    
        _result[0] = node;
        _result[1] = parent;
        _result[2] = message;
        _result[3] = messageParent;
        return _result;
    }
    
    function setNode(
        root, parent, node, messageRoot, messageParent, message,
        key, branch, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector, replacedPaths) {
    
        var type = node.$type;
    
        while (type === $ref) {
    
            var results = setReference(
                root, node, messageRoot, message, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector, replacedPaths
            );
    
            node = results[0];
    
            if (isPrimitive(node)) {
                return results;
            }
    
            parent = results[1];
            message = results[2];
            messageParent = results[3];
            type = node.$type;
        }
    
        if (type !== void 0) {
            _result[0] = node;
            _result[1] = parent;
            _result[2] = message;
            _result[3] = messageParent;
            return _result;
        }
    
        if (key == null) {
            if (branch) {
                throw new NullInPathError();
            } else if (node) {
                key = node.$_key;
            }
        } else {
            parent = node;
            messageParent = message;
            node = parent[key];
            message = messageParent && messageParent[key];
        }
    
        node = mergeJSONGraphNode(
            parent, node, message, key, requestedPath, optimizedPath,
            version, expired, lru, comparator, errorSelector, replacedPaths
        );
    
        _result[0] = node;
        _result[1] = parent;
        _result[2] = message;
        _result[3] = messageParent;
        return _result;
    }
    
    ================================================ FILE: doc/set_setPathMaps.js.html ================================================ --- layout: api-page title: "set/setPathMaps.js" id: api ---

    set/setPathMaps.js

    var createHardlink = require("./../support/createHardlink");
    var __prefix = require("./../internal/reservedPrefix");
    var $ref = require("./../types/ref");
    
    var getBoundValue = require("./../get/getBoundValue");
    
    var isArray = Array.isArray;
    var hasOwn = require("./../support/hasOwn");
    var isObject = require("./../support/isObject");
    var isExpired = require("./../support/isExpired");
    var isFunction = require("./../support/isFunction");
    var isPrimitive = require("./../support/isPrimitive");
    var expireNode = require("./../support/expireNode");
    var incrementVersion = require("./../support/incrementVersion");
    var mergeValueOrInsertBranch = require("./../support/mergeValueOrInsertBranch");
    var NullInPathError = require("./../errors/NullInPathError");
    
    /**
     * Sets a list of {@link PathMapEnvelope}s into a {@link JSONGraph}.
     * @function
     * @param {Object} model - the Model for which to insert the PathMaps.
     * @param {Array.<PathMapEnvelope>} pathMapEnvelopes - the a list of {@link PathMapEnvelope}s to set.
     * @return {Array.<Array.<Path>>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values.
     */
    
    module.exports = function setPathMaps(model, pathMapEnvelopes, x, errorSelector, comparator) {
    
        var modelRoot = model._root;
        var lru = modelRoot;
        var expired = modelRoot.expired;
        var version = incrementVersion();
        var bound = model._path;
        var cache = modelRoot.cache;
        var node = bound.length ? getBoundValue(model, bound).value : cache;
        var parent = node.$_parent || cache;
        var initialVersion = cache.$_version;
    
        var requestedPath = [];
        var requestedPaths = [];
        var optimizedPaths = [];
        var optimizedIndex = bound.length;
        var pathMapIndex = -1;
        var pathMapCount = pathMapEnvelopes.length;
    
        while (++pathMapIndex < pathMapCount) {
    
            var pathMapEnvelope = pathMapEnvelopes[pathMapIndex];
            var optimizedPath = bound.slice(0);
            optimizedPath.index = optimizedIndex;
    
            setPathMap(
                pathMapEnvelope.json, 0, cache, parent, node,
                requestedPaths, optimizedPaths, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector
            );
        }
    
        var newVersion = cache.$_version;
        var rootChangeHandler = modelRoot.onChange;
    
        if (isFunction(rootChangeHandler) && initialVersion !== newVersion) {
            rootChangeHandler();
        }
    
        return [requestedPaths, optimizedPaths];
    };
    
    /* eslint-disable no-constant-condition */
    function setPathMap(
        pathMap, depth, root, parent, node,
        requestedPaths, optimizedPaths, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector) {
    
        var keys = getKeys(pathMap);
    
        if (keys && keys.length) {
    
            var keyIndex = 0;
            var keyCount = keys.length;
            var optimizedIndex = optimizedPath.index;
    
            do {
                var key = keys[keyIndex];
                var child = pathMap[key];
                var branch = isObject(child) && !child.$type;
    
                requestedPath.depth = depth;
    
                var results = setNode(
                    root, parent, node, key, child,
                    branch, false, requestedPath, optimizedPath,
                    version, expired, lru, comparator, errorSelector
                );
    
                requestedPath[depth] = key;
                requestedPath.index = depth;
    
                optimizedPath[optimizedPath.index++] = key;
                var nextNode = results[0];
                var nextParent = results[1];
                if (nextNode) {
                    if (branch) {
                        setPathMap(
                            child, depth + 1,
                            root, nextParent, nextNode,
                            requestedPaths, optimizedPaths, requestedPath, optimizedPath,
                            version, expired, lru, comparator, errorSelector
                        );
                    } else {
                        requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1));
                        optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index));
                    }
                }
                if (++keyIndex >= keyCount) {
                    break;
                }
                optimizedPath.index = optimizedIndex;
            } while (true);
        }
    }
    /* eslint-enable */
    
    function setReference(
        value, root, node, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector) {
    
        var reference = node.value;
        optimizedPath.length = 0;
        optimizedPath.push.apply(optimizedPath, reference);
    
        if (isExpired(node)) {
            optimizedPath.index = reference.length;
            expireNode(node, expired, lru);
            return [undefined, root];
        }
    
        var container = node;
        var parent = root;
    
        node = node.$_context;
    
        if (node != null) {
            parent = node.$_parent || root;
            optimizedPath.index = reference.length;
        } else {
    
            var index = 0;
            var count = reference.length - 1;
            optimizedPath.index = index;
    
            parent = node = root;
    
            do {
                var key = reference[index];
                var branch = index < count;
                var results = setNode(
                    root, parent, node, key, value,
                    branch, true, requestedPath, optimizedPath,
                    version, expired, lru, comparator, errorSelector
                );
                node = results[0];
                if (isPrimitive(node)) {
                    optimizedPath.index = index;
                    return results;
                }
                parent = results[1];
            } while (index++ < count);
    
            optimizedPath.index = index;
    
            if (container.$_context !== node) {
                createHardlink(container, node);
            }
        }
    
        return [node, parent];
    }
    
    function setNode(
        root, parent, node, key, value,
        branch, reference, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector) {
    
        var type = node.$type;
    
        while (type === $ref) {
    
            var results = setReference(
                value, root, node, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector);
    
            node = results[0];
    
            if (isPrimitive(node)) {
                return results;
            }
    
            parent = results[1];
            type = node && node.$type;
        }
    
        if (type !== void 0) {
            return [node, parent];
        }
    
        if (key == null) {
            if (branch) {
                throw new NullInPathError();
            } else if (node) {
                key = node.$_key;
            }
        } else {
            parent = node;
            node = parent[key];
        }
    
        node = mergeValueOrInsertBranch(
            parent, node, key, value,
            branch, reference, requestedPath, optimizedPath,
            version, expired, lru, comparator, errorSelector
        );
    
        return [node, parent];
    }
    
    function getKeys(pathMap) {
    
        if (isObject(pathMap) && !pathMap.$type) {
            var keys = [];
            var itr = 0;
            if (isArray(pathMap)) {
                keys[itr++] = "length";
            }
            for (var key in pathMap) {
                if (key[0] === __prefix || !hasOwn(pathMap, key)) {
                    continue;
                }
                keys[itr++] = key;
            }
            return keys;
        }
    
        return void 0;
    }
    
    ================================================ FILE: doc/set_setPathValues.js.html ================================================ --- layout: api-page title: "set/setPathValues.js" id: api ---

    set/setPathValues.js

    var createHardlink = require("./../support/createHardlink");
    var $ref = require("./../types/ref");
    
    var getBoundValue = require("./../get/getBoundValue");
    
    var isExpired = require("./../support/isExpired");
    var isFunction = require("./../support/isFunction");
    var isPrimitive = require("./../support/isPrimitive");
    var expireNode = require("./../support/expireNode");
    var iterateKeySet = require("falcor-path-utils").iterateKeySet;
    var incrementVersion = require("./../support/incrementVersion");
    var mergeValueOrInsertBranch = require("./../support/mergeValueOrInsertBranch");
    var NullInPathError = require("./../errors/NullInPathError");
    
    /**
     * Sets a list of {@link PathValue}s into a {@link JSONGraph}.
     * @function
     * @param {Object} model - the Model for which to insert the {@link PathValue}s.
     * @param {Array.<PathValue>} pathValues - the list of {@link PathValue}s to set.
     * @return {Array.<Array.<Path>>} - an Array of Arrays where each inner Array is a list of requested and optimized paths (respectively) for the successfully set values.
     */
    
    module.exports = function setPathValues(model, pathValues, x, errorSelector, comparator) {
    
        var modelRoot = model._root;
        var lru = modelRoot;
        var expired = modelRoot.expired;
        var version = incrementVersion();
        var bound = model._path;
        var cache = modelRoot.cache;
        var node = bound.length ? getBoundValue(model, bound).value : cache;
        var parent = node.$_parent || cache;
        var initialVersion = cache.$_version;
    
        var requestedPath = [];
        var requestedPaths = [];
        var optimizedPaths = [];
        var optimizedIndex = bound.length;
        var pathValueIndex = -1;
        var pathValueCount = pathValues.length;
    
        while (++pathValueIndex < pathValueCount) {
    
            var pathValue = pathValues[pathValueIndex];
            var path = pathValue.path;
            var value = pathValue.value;
            var optimizedPath = bound.slice(0);
            optimizedPath.index = optimizedIndex;
    
            setPathSet(
                value, path, 0, cache, parent, node,
                requestedPaths, optimizedPaths, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector
            );
        }
    
        var newVersion = cache.$_version;
        var rootChangeHandler = modelRoot.onChange;
    
        if (isFunction(rootChangeHandler) && initialVersion !== newVersion) {
            rootChangeHandler();
        }
    
        return [requestedPaths, optimizedPaths];
    };
    
    /* eslint-disable no-constant-condition */
    function setPathSet(
        value, path, depth, root, parent, node,
        requestedPaths, optimizedPaths, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector, replacedPaths) {
    
        var note = {};
        var branch = depth < path.length - 1;
        var keySet = path[depth];
        var key = iterateKeySet(keySet, note);
        var optimizedIndex = optimizedPath.index;
    
        do {
    
            requestedPath.depth = depth;
    
            var results = setNode(
                root, parent, node, key, value,
                branch, false, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector, replacedPaths
            );
            requestedPath[depth] = key;
            requestedPath.index = depth;
            optimizedPath[optimizedPath.index++] = key;
            var nextNode = results[0];
            var nextParent = results[1];
            if (nextNode) {
                if (branch) {
                    setPathSet(
                        value, path, depth + 1,
                        root, nextParent, nextNode,
                        requestedPaths, optimizedPaths, requestedPath, optimizedPath,
                        version, expired, lru, comparator, errorSelector
                    );
                } else {
                    requestedPaths.push(requestedPath.slice(0, requestedPath.index + 1));
                    optimizedPaths.push(optimizedPath.slice(0, optimizedPath.index));
                }
            }
            key = iterateKeySet(keySet, note);
            if (note.done) {
                break;
            }
            optimizedPath.index = optimizedIndex;
        } while (true);
    }
    /* eslint-enable */
    
    function setReference(
        value, root, node, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector, replacedPaths) {
    
        var reference = node.value;
        optimizedPath.length = 0;
        optimizedPath.push.apply(optimizedPath, reference);
    
        if (isExpired(node)) {
            optimizedPath.index = reference.length;
            expireNode(node, expired, lru);
            return [undefined, root];
        }
    
        var container = node;
        var parent = root;
    
        node = node.$_context;
    
        if (node != null) {
            parent = node.$_parent || root;
            optimizedPath.index = reference.length;
        } else {
    
            var index = 0;
            var count = reference.length - 1;
    
            parent = node = root;
    
            do {
                var key = reference[index];
                var branch = index < count;
                optimizedPath.index = index;
    
                var results = setNode(
                    root, parent, node, key, value,
                    branch, true, requestedPath, optimizedPath,
                    version, expired, lru, comparator, errorSelector, replacedPaths
                );
                node = results[0];
                if (isPrimitive(node)) {
                    optimizedPath.index = index;
                    return results;
                }
                parent = results[1];
            } while (index++ < count);
    
            optimizedPath.index = index;
    
            if (container.$_context !== node) {
                createHardlink(container, node);
            }
        }
    
        return [node, parent];
    }
    
    function setNode(
        root, parent, node, key, value,
        branch, reference, requestedPath, optimizedPath,
        version, expired, lru, comparator, errorSelector, replacedPaths) {
    
        var type = node.$type;
    
        while (type === $ref) {
    
            var results = setReference(
                value, root, node, requestedPath, optimizedPath,
                version, expired, lru, comparator, errorSelector, replacedPaths
            );
    
            node = results[0];
    
            if (isPrimitive(node)) {
                return results;
            }
    
            parent = results[1];
            type = node.$type;
        }
    
        if (branch && type !== void 0) {
            return [node, parent];
        }
    
        if (key == null) {
            if (branch) {
                throw new NullInPathError();
            } else if (node) {
                key = node.$_key;
            }
        } else {
            parent = node;
            node = parent[key];
        }
    
        node = mergeValueOrInsertBranch(
            parent, node, key, value,
            branch, reference, requestedPath, optimizedPath,
            version, expired, lru, comparator, errorSelector, replacedPaths
        );
    
        return [node, parent];
    }
    
    ================================================ FILE: doc/support_reconstructPath.js.html ================================================ --- layout: api-page title: "support/reconstructPath.js" id: api ---

    support/reconstructPath.js

    /**
     * Reconstructs the path for the current key, from currentPath (requestedPath)
     * state maintained during set/merge walk operations.
     *
     * During the walk, since the requestedPath array is updated after we attempt to
     * merge/insert nodes during a walk (it reflects the inserted node's parent branch)
     * we need to reconstitute a path from it.
     *
     * @param  {Array} currentPath The current requestedPath state, during the walk
     * @param  {String} key        The current key value, during the walk
     * @return {Array} A new array, with the path which represents the node we're about
     * to insert
     */
    module.exports = function reconstructPath(currentPath, key) {
    
        var path = currentPath.slice(0, currentPath.depth);
        path[path.length] = key;
    
        return path;
    };
    
    ================================================ FILE: doc/toEsObservable.js.html ================================================ --- layout: api-page title: "toEsObservable.js" id: api ---

    toEsObservable.js

    /**
     * FromEsObserverAdapter is an adpater from an ES Observer to an Rx 2 Observer
     * @constructor FromEsObserverAdapter
    */
    function FromEsObserverAdapter(esObserver) {
        this.esObserver = esObserver;
    }
    
    FromEsObserverAdapter.prototype = {
        onNext: function onNext(value) {
            if (typeof this.esObserver.next === "function") {
                this.esObserver.next(value);
            }
        },
        onError: function onError(error) {
            if (typeof this.esObserver.error === "function") {
                this.esObserver.error(error);
            }
        },
        onCompleted: function onCompleted() {
            if (typeof this.esObserver.complete === "function") {
                this.esObserver.complete();
            }
        }
    };
    
    /**
     * ToEsSubscriptionAdapter is an adpater from the Rx 2 subscription to the ES subscription
     * @constructor ToEsSubscriptionAdapter
    */
    function ToEsSubscriptionAdapter(subscription) {
        this.subscription = subscription;
    }
    
    ToEsSubscriptionAdapter.prototype.unsubscribe = function unsubscribe() {
        this.subscription.dispose();
    };
    
    
    function toEsObservable(_self) {
        return {
            subscribe: function subscribe(observer) {
                return new ToEsSubscriptionAdapter(_self.subscribe(new FromEsObserverAdapter(observer)));
            }
        };
    }
    
    module.exports = toEsObservable;
    
    ================================================ FILE: doc/typedefs_Atom.js.html ================================================ --- layout: api-page title: "typedefs/Atom.js" id: api ---

    typedefs/Atom.js

    /**
     * An atom allows you to treat a JSON value as atomic regardless of its type, ensuring that a JSON object or array is always returned in its entirety. The JSON value must be treated as immutable. Atoms can also be used to associate metadata with a JSON value. This metadata can be used to influence the way values are handled.
     * @typedef {Object} Atom
     * @property {!String} $type - the $type must be "atom"
     * @property {!*} value - the immutable JSON value
     * @property {number} [$expires] - the time to expire in milliseconds
     *  - positive number: expires in milliseconds since epoch
     *  - negative number: expires relative to when the Atom is merged into the JSONGraph
     *  - number 1: never expires
     * @example
     // Atom with number value, expiring in 2 seconds
     {
        $type: "atom",
        value: 5
        $expires: -2000
     }
     // Atom with Object value that never expires
     {
        $type: "atom",
        value: {
            foo: 5,
            bar: "baz"
        },
        $expires: 1
     }
     */
    
    ================================================ FILE: doc/typedefs_DataSource.js.html ================================================ --- layout: api-page title: "typedefs/DataSource.js" id: api ---

    typedefs/DataSource.js

    /**
     * A DataSource is an interface which can be implemented to expose JSON Graph information to a Model. Every DataSource is associated with a single JSON Graph object. Models execute JSON Graph operations (get, set, and call) to retrieve values from the DataSource’s JSON Graph object. DataSources may retrieve JSON Graph information from anywhere, including device memory, a remote machine, or even a lazily-run computation.
     * @constructor DataSource
     * @abstract
     */
    
    /**
     * The get method retrieves values from the DataSource's associated JSONGraph object.
     * @name get
     * @function
     * @arg {Array.<PathSet>} pathSets the path(s) to retrieve
     * @returns {Observable.<JSONGraphEnvelope>} jsonGraphEnvelope the response returned from the server.
     * @memberof DataSource.prototype
     */
    
    /**
     * The set method accepts values to set in the DataSource's associated JSONGraph object.
     * @name set
     * @function
     * @arg {JSONGraphEnvelope} jsonGraphEnvelope a JSONGraphEnvelope containing values to set in the DataSource's associated JSONGraph object.
     * @returns {Observable.<JSONGraphEnvelope>} a JSONGraphEnvelope containing all of the requested values after the set operation.
     * @memberof DataSource.prototype
     */
    
    /**
     * Invokes a function in the DataSource's JSONGraph object.
     * @name call
     * @function
     * @arg {Path} functionPath the path to the function to invoke
     * @arg {Array.<Object>} args the arguments to pass to the function
     * @arg {Array.<PathSet>} refSuffixes paths to retrieve from the targets of JSONGraph References in the function's response.
     * @arg {Array.<PathSet>} extraPaths additional paths to retrieve after successful function execution
     * @returns {Observable.<JSONGraphEnvelope>} jsonGraphEnvelope the response returned from the server.
     * @memberof DataSource.prototype
     */
    
    ================================================ FILE: doc/typedefs_JSONEnvelope.js.html ================================================ --- layout: api-page title: "typedefs/JSONEnvelope.js" id: api ---

    typedefs/JSONEnvelope.js

    /**
     * An envelope that wraps a JSON object.
     * @typedef {Object} JSONEnvelope
     * @property {JSON} json - a JSON object
     * @example
     var model = new falcor.Model();
     model.set({
        json: {
          name: "Steve",
          surname: "McGuire"
        }
     }).then(function(jsonEnvelope) {
        console.log(jsonEnvelope);
     });
     */
    
    ================================================ FILE: doc/typedefs_JSONGraph.js.html ================================================ --- layout: api-page title: "typedefs/JSONGraph.js" id: api ---

    typedefs/JSONGraph.js

    /**
     * JavaScript Object Notation Graph (JSONGraph) is a notation for expressing graphs in JSON. For more information, see the [JSONGraph Guide]{@link http://netflix.github.io/falcor/documentation/jsongraph.html}.
     * @typedef {Object} JSONGraph
     * @example
     var $ref = falcor.ref;
     // JSONGraph model modeling a list of film genres, each of which contains the same title.
     {
        // list of user's genres, modeled as a map with ordinal keys
        "genreLists": {
            "0": $ref('genresById[123]'),
            "1": $ref('genresById[522]'),
            "length": 2
        },
        // map of all genres, organized by ID
        "genresById": {
            // genre list modeled as map with ordinal keys
            "123": {
                "name": "Drama",
                "0": $ref('titlesById[23]'),
                "1": $ref('titlesById[99]'),
                "length": 2
            },
            // genre list modeled as map with ordinal keys
            "522": {
                "name": "Comedy",
                "0": $ref('titlesById[23]'),
                "1": $ref('titlesById[44]'),
                "length": 2
            }
        },
        // map of all titles, organized by ID
        "titlesById": {
           "99": {
                "name": "House of Cards",
                "rating": 5
            },
            "23": {
                "name": "Orange is the New Black",
                "rating": 5
            },
            "44": {
                "name": "Arrested Development",
                "rating": 5
            }
        }
    }
    */
    
    ================================================ FILE: doc/typedefs_JSONGraphEnvelope.js.html ================================================ --- layout: api-page title: "typedefs/JSONGraphEnvelope.js" id: api ---

    typedefs/JSONGraphEnvelope.js

    /**
     * An envelope that wraps a {@link JSONGraph} fragment.
     * @typedef {Object} JSONGraphEnvelope
     * @property {JSONGraph} jsonGraph - a {@link JSONGraph} fragment
     * @property {?Array.<PathSet>} paths - the paths to the values in the {@link JSONGraph} fragment
     * @property {?Array.<PathSet>} invalidated - the paths to invalidate in the {@link Model}
     * @example
    var $ref = falcor.ref;
    var model = new falcor.Model();
    model.set({
      paths: [
        ["todos", [0,1], ["name","done"]]
      ],
      jsonGraph: {
        todos: [
          $ref("todosById[12]"),
          $ref("todosById[15]")
        ],
        todosById: {
          12: {
            name: "go to the ATM",
            done: false
          },
          15: {
            name: "buy milk",
            done: false
          }
        }
      },
    }).then(function(jsonEnvelope) {
      console.log(JSON.stringify(jsonEnvelope, null, 4));
    });
    
    // prints...
    // {
    //   json: {
    //     todos: {
    //       0: {
    //         name: "go to the ATM",
    //         done: false
    //       },
    //       1: {
    //         name: "buy milk",
    //         done: false
    //       }
    //     }
    //   }
    // }
     */
    
    ================================================ FILE: doc/typedefs_Key.js.html ================================================ --- layout: api-page title: "typedefs/Key.js" id: api ---

    typedefs/Key.js

    /**
     * A part of a {@link Path} that can be any JSON value type. All types are coerced to string, except null. This makes the number 1 and the string "1" equivalent.
     * @typedef {?(string|number|boolean|null)} Key
     */
    
    ================================================ FILE: doc/typedefs_KeySet.js.html ================================================ --- layout: api-page title: "typedefs/KeySet.js" id: api ---

    typedefs/KeySet.js

    /**
     * A part of a {@link PathSet} that can be either a {@link Key}, {@link Range}, or Array of either.
     * @typedef {(Key|Range|Array.<(Key|Range)>)} KeySet
     */
    
    ================================================ FILE: doc/typedefs_Observable.js.html ================================================ --- layout: api-page title: "typedefs/Observable.js" id: api ---

    typedefs/Observable.js

    
    /**
     * @constructor Observable
     */
    
     /**
     * The forEach method is a synonym for {@link Observable.prototype.subscribe} and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us.  These values can be received using either callbacks or an {@link Observer} object.
     * @name forEach
     * @memberof Observable.prototype
     * @function
     * @arg {?Observable~onNextCallback} onNext a callback that accepts the next value in the stream of values
     * @arg {?Observable~onErrorCallback} onError a callback that accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream
     * @arg {?Observable~onCompletedCallback} onCompleted a callback that is invoked when the {@link Observable} stream has ended, and the {@link Observable~onNextCallback} will not receive any more values
     * @return {Subscription}
     */
    
     /**
     * The subscribe method is a synonym for {@link Observable.prototype.forEach} and triggers the execution of the Observable, causing the values within to be pushed to a callback. An Observable is like a pipe of water that is closed. When forEach is called, we open the valve and the values within are pushed at us.  These values can be received using either callbacks or an {@link Observer} object.
     * @name subscribe
     * @memberof Observable.prototype
     * @function
     * @arg {?Observable~onNextCallback} onNext a callback that accepts the next value in the stream of values
     * @arg {?Observable~onErrorCallback} onError a callback that accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream
     * @arg {?Observable~onCompletedCallback} onCompleted a callback that is invoked when the {@link Observable} stream has ended, and the {@link Observable~onNextCallback} will not receive any more values
     * @return {Subscription}
     */
    
    /**
     * This callback accepts a value that was emitted while evaluating the operation underlying the {@link Observable} stream.
     * @callback Observable~onNextCallback
     * @param {Object} value the value that was emitted while evaluating the operation underlying the {@link Observable}
     */
    
    /**
     * This callback accepts an error that occurred while evaluating the operation underlying the {@link Observable} stream. When this callback is invoked, the {@link Observable} stream ends and no more values will be received by the {@link Observable~onNextCallback}.
     * @callback Observable~onErrorCallback
     * @param {Error} error the error that occurred while evaluating the operation underlying the {@link Observable}
     */
    
     /**
     * This callback is invoked when the {@link Observable} stream ends. When this callback is invoked the {@link Observable} stream has ended, and therefore the {@link Observable~onNextCallback} will not receive any more values.
     * @callback Observable~onCompletedCallback
     */
    
    /**
     * @constructor Subscription
     * @see {@link https://github.com/Reactive-Extensions/RxJS/tree/master/doc}
     */
    
    /**
     * When this method is called on the Subscription, the Observable that created the Subscription will stop sending values to the callbacks passed when the Subscription was created.
     * @name dispose
     * @method
     * @memberof Subscription.prototype
     */
    
    ================================================ FILE: doc/typedefs_Path.js.html ================================================ --- layout: api-page title: "typedefs/Path.js" id: api ---

    typedefs/Path.js

    /**
     * An ordered list of {@link Key}s that point to a value in a {@link JSONGraph}.
     * @typedef {Array.<Key>} Path
     * @example
     // Points to the name of product 1234
     ["productsById", "1234", "name"]
     */
    
    ================================================ FILE: doc/typedefs_PathSet.js.html ================================================ --- layout: api-page title: "typedefs/PathSet.js" id: api ---

    typedefs/PathSet.js

    /**
     * An ordered list of {@link KeySet}s that point to location(s) in the {@link JSONGraph}. It enables pointing to multiple locations in a more terse format than a set of {@link Path}s and is generally more efficient to evaluate.
     * @typedef {Array.<KeySet>} PathSet
     * @example
     // Points to the name and price of products 1234 and 5678
     ["productsById", ["1234", "5678"], ["name", "price"]]
     */
    
    ================================================ FILE: doc/typedefs_PathValue.js.html ================================================ --- layout: api-page title: "typedefs/PathValue.js" id: api ---

    typedefs/PathValue.js

    /**
     * A wrapper around a path and its value.
     * @typedef {Object} PathValue
     * @property {PathSet} path - the path to a location in the {@link JSONGraph}
     * @property {?*} value - the value of that path
     * @example
     {
    	path: ["productsById", "1234", "name"],
    	value: "ABC"
     }
     */
    
    ================================================ FILE: doc/typedefs_Range.js.html ================================================ --- layout: api-page title: "typedefs/Range.js" id: api ---

    typedefs/Range.js

    /**
     * Describe a range of integers. Must contain either a "to" or "length" property.
     * @typedef {Object} Range
     * @property {number} [from=0] - the lower bound of the range (inclusive)
     * @property {?number} to - the upper bound of the range (inclusive). Must be >= to the "from" value
     * @property {?number} length - the length of the range. Must be >= 0
     * @example
     // The following range specifies the numbers 0, 1, and 2
     {from: 0, to: 2}
     // The following range specifies the numbers 1 and 2
     {from: 1, length: 2}
     */
    
    ================================================ FILE: examples/datasource/webWorkerSource.js ================================================ // In this example we demonstrate the communication between a model source and a server over a web worker // Below you will find both the code to be run in a worker and the code to include in the html in a //