[
  {
    "path": ".github/workflows/main.yml",
    "content": "name: Clojure CI\n\non: [push, pull_request]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n    \n    steps:\n    - uses: actions/checkout@v1\n    - name: Install npm dependencies\n      run: npm install\n    - name: Install Clojure dependencies\n      run: lein deps\n    - name: Run tests\n      run: lein kaocha\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\n/out\n/node_modules\npom.xml\npom.xml.asc\n/.lein-*\n/.nrepl-port\n/.cpcache\n/.cljs\n/node_modules\n.cljs_node_repl\n/.lumo_cache/\n.idea/\n*.iml\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 0.0.0-alpha13 (2020-05-18)\n# 0.0.0-alpha12 (2020-05-18)\n# 0.0.0-alpha11 (2020-05-18)\n\n* rebuild without dev-dependencies.\n\n# 0.0.0-alpha10 (2020-05-15)\n\n* Fail nicely (throw or return error) for unsupported Contexts.\n   * e.g. returning a `core.async` channel without enabling it's support by requiring `sieppari.async.core-async`\n\n# 0.0.0-alpha9 (2020-05-04)\n\n* Fix performance regressions from previous Alphas. \n* Tested against Promesa 5.*.\n\n# 0.0.0-alpha8 (2019-11-06)\n\n* Support `java.util.concurrent.CompletionStage` by default on the JVM and\n  `js/Promise` for ClojureScript.\n  * `sieppari.async.promesa` is not needed anymore and has been removed.\n* Catch Async Exceptions\n* Support Promesa 4.x (thanks to by [Andrea Richiardi](https://github.com/arichiardi) and [Andrey Antukh](https://github.com/niwinz))\n* Remove automatic support for 3rd party async libs\n  * Reduced `sieppari.core` load time by about 4 seconds.\n  * `sieppari.async.*` namespaces now need to be `require`:d explicitly, you probably only need one of them.\n* Run tests with Kaocha on JVM and node, then consolidate build tools back to just Leiningen.\n\n# 0.0.0-alpha7 (2018-12-29)\n\n* Initial support for ClojureScript by [Andrea Richiardi](https://github.com/arichiardi)\n  * `core.async` & `promesa` (requires `1.10.0-SNAPSHOT`)\n"
  },
  {
    "path": "HACKING.md",
    "content": "# Testing\n\n## CLI\n\n```shell script\nlein kaocha # run clj and cljs tests\n```\n\n`kaocha-cljs` [requires](https://github.com/lambdaisland/kaocha-cljs#quickstart) `ws` from npm.\nA `package(-lock).json` is provided so you can just `npm install` those.\n \n## REPL\n\n```clojure\n(kaocha/run :unit) # `user` ns has alias `kaocha` -> `kaocha.repl`\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "Eclipse Public License - v 2.0\n\n    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE\n    PUBLIC LICENSE (\"AGREEMENT\"). ANY USE, REPRODUCTION OR DISTRIBUTION\n    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.\n\n1. DEFINITIONS\n\n\"Contribution\" means:\n\n  a) in the case of the initial Contributor, the initial content\n     Distributed under this Agreement, and\n\n  b) in the case of each subsequent Contributor:\n     i) changes to the Program, and\n     ii) additions to the Program;\n  where such changes and/or additions to the Program originate from\n  and are Distributed by that particular Contributor. A Contribution\n  \"originates\" from a Contributor if it was added to the Program by\n  such Contributor itself or anyone acting on such Contributor's behalf.\n  Contributions do not include changes or additions to the Program that\n  are not Modified Works.\n\n\"Contributor\" means any person or entity that Distributes the Program.\n\n\"Licensed Patents\" mean patent claims licensable by a Contributor which\nare necessarily infringed by the use or sale of its Contribution alone\nor when combined with the Program.\n\n\"Program\" means the Contributions Distributed in accordance with this\nAgreement.\n\n\"Recipient\" means anyone who receives the Program under this Agreement\nor any Secondary License (as applicable), including Contributors.\n\n\"Derivative Works\" shall mean any work, whether in Source Code or other\nform, that is based on (or derived from) the Program and for which the\neditorial revisions, annotations, elaborations, or other modifications\nrepresent, as a whole, an original work of authorship.\n\n\"Modified Works\" shall mean any work in Source Code or other form that\nresults from an addition to, deletion from, or modification of the\ncontents of the Program, including, for purposes of clarity any new file\nin Source Code form that contains any contents of the Program. Modified\nWorks shall not include works that contain only declarations,\ninterfaces, types, classes, structures, or files of the Program solely\nin each case in order to link to, bind by name, or subclass the Program\nor Modified Works thereof.\n\n\"Distribute\" means the acts of a) distributing or b) making available\nin any manner that enables the transfer of a copy.\n\n\"Source Code\" means the form of a Program preferred for making\nmodifications, including but not limited to software source code,\ndocumentation source, and configuration files.\n\n\"Secondary License\" means either the GNU General Public License,\nVersion 2.0, or any later versions of that license, including any\nexceptions or additional permissions as identified by the initial\nContributor.\n\n2. GRANT OF RIGHTS\n\n  a) Subject to the terms of this Agreement, each Contributor hereby\n  grants Recipient a non-exclusive, worldwide, royalty-free copyright\n  license to reproduce, prepare Derivative Works of, publicly display,\n  publicly perform, Distribute and sublicense the Contribution of such\n  Contributor, if any, and such Derivative Works.\n\n  b) Subject to the terms of this Agreement, each Contributor hereby\n  grants Recipient a non-exclusive, worldwide, royalty-free patent\n  license under Licensed Patents to make, use, sell, offer to sell,\n  import and otherwise transfer the Contribution of such Contributor,\n  if any, in Source Code or other form. This patent license shall\n  apply to the combination of the Contribution and the Program if, at\n  the time the Contribution is added by the Contributor, such addition\n  of the Contribution causes such combination to be covered by the\n  Licensed Patents. The patent license shall not apply to any other\n  combinations which include the Contribution. No hardware per se is\n  licensed hereunder.\n\n  c) Recipient understands that although each Contributor grants the\n  licenses to its Contributions set forth herein, no assurances are\n  provided by any Contributor that the Program does not infringe the\n  patent or other intellectual property rights of any other entity.\n  Each Contributor disclaims any liability to Recipient for claims\n  brought by any other entity based on infringement of intellectual\n  property rights or otherwise. As a condition to exercising the\n  rights and licenses granted hereunder, each Recipient hereby\n  assumes sole responsibility to secure any other intellectual\n  property rights needed, if any. For example, if a third party\n  patent license is required to allow Recipient to Distribute the\n  Program, it is Recipient's responsibility to acquire that license\n  before distributing the Program.\n\n  d) Each Contributor represents that to its knowledge it has\n  sufficient copyright rights in its Contribution, if any, to grant\n  the copyright license set forth in this Agreement.\n\n  e) Notwithstanding the terms of any Secondary License, no\n  Contributor makes additional grants to any Recipient (other than\n  those set forth in this Agreement) as a result of such Recipient's\n  receipt of the Program under the terms of a Secondary License\n  (if permitted under the terms of Section 3).\n\n3. REQUIREMENTS\n\n3.1 If a Contributor Distributes the Program in any form, then:\n\n  a) the Program must also be made available as Source Code, in\n  accordance with section 3.2, and the Contributor must accompany\n  the Program with a statement that the Source Code for the Program\n  is available under this Agreement, and informs Recipients how to\n  obtain it in a reasonable manner on or through a medium customarily\n  used for software exchange; and\n\n  b) the Contributor may Distribute the Program under a license\n  different than this Agreement, provided that such license:\n     i) effectively disclaims on behalf of all other Contributors all\n     warranties and conditions, express and implied, including\n     warranties or conditions of title and non-infringement, and\n     implied warranties or conditions of merchantability and fitness\n     for a particular purpose;\n\n     ii) effectively excludes on behalf of all other Contributors all\n     liability for damages, including direct, indirect, special,\n     incidental and consequential damages, such as lost profits;\n\n     iii) does not attempt to limit or alter the recipients' rights\n     in the Source Code under section 3.2; and\n\n     iv) requires any subsequent distribution of the Program by any\n     party to be under a license that satisfies the requirements\n     of this section 3.\n\n3.2 When the Program is Distributed as Source Code:\n\n  a) it must be made available under this Agreement, or if the\n  Program (i) is combined with other material in a separate file or\n  files made available under a Secondary License, and (ii) the initial\n  Contributor attached to the Source Code the notice described in\n  Exhibit A of this Agreement, then the Program may be made available\n  under the terms of such Secondary Licenses, and\n\n  b) a copy of this Agreement must be included with each copy of\n  the Program.\n\n3.3 Contributors may not remove or alter any copyright, patent,\ntrademark, attribution notices, disclaimers of warranty, or limitations\nof liability (\"notices\") contained within the Program from any copy of\nthe Program which they Distribute, provided that Contributors may add\ntheir own appropriate notices.\n\n4. COMMERCIAL DISTRIBUTION\n\nCommercial distributors of software may accept certain responsibilities\nwith respect to end users, business partners and the like. While this\nlicense is intended to facilitate the commercial use of the Program,\nthe Contributor who includes the Program in a commercial product\noffering should do so in a manner which does not create potential\nliability for other Contributors. Therefore, if a Contributor includes\nthe Program in a commercial product offering, such Contributor\n(\"Commercial Contributor\") hereby agrees to defend and indemnify every\nother Contributor (\"Indemnified Contributor\") against any losses,\ndamages and costs (collectively \"Losses\") arising from claims, lawsuits\nand other legal actions brought by a third party against the Indemnified\nContributor to the extent caused by the acts or omissions of such\nCommercial Contributor in connection with its distribution of the Program\nin a commercial product offering. The obligations in this section do not\napply to any claims or Losses relating to any actual or alleged\nintellectual property infringement. In order to qualify, an Indemnified\nContributor must: a) promptly notify the Commercial Contributor in\nwriting of such claim, and b) allow the Commercial Contributor to control,\nand cooperate with the Commercial Contributor in, the defense and any\nrelated settlement negotiations. The Indemnified Contributor may\nparticipate in any such claim at its own expense.\n\nFor example, a Contributor might include the Program in a commercial\nproduct offering, Product X. That Contributor is then a Commercial\nContributor. If that Commercial Contributor then makes performance\nclaims, or offers warranties related to Product X, those performance\nclaims and warranties are such Commercial Contributor's responsibility\nalone. Under this section, the Commercial Contributor would have to\ndefend claims against the other Contributors related to those performance\nclaims and warranties, and if a court requires any other Contributor to\npay any damages as a result, the Commercial Contributor must pay\nthose damages.\n\n5. NO WARRANTY\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT\nPERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN \"AS IS\"\nBASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR\nIMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF\nTITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR\nPURPOSE. Each Recipient is solely responsible for determining the\nappropriateness of using and distributing the Program and assumes all\nrisks associated with its exercise of rights under this Agreement,\nincluding but not limited to the risks and costs of program errors,\ncompliance with applicable laws, damage to or loss of data, programs\nor equipment, and unavailability or interruption of operations.\n\n6. DISCLAIMER OF LIABILITY\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT\nPERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS\nSHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST\nPROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE\nEXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n7. GENERAL\n\nIf any provision of this Agreement is invalid or unenforceable under\napplicable law, it shall not affect the validity or enforceability of\nthe remainder of the terms of this Agreement, and without further\naction by the parties hereto, such provision shall be reformed to the\nminimum extent necessary to make such provision valid and enforceable.\n\nIf Recipient institutes patent litigation against any entity\n(including a cross-claim or counterclaim in a lawsuit) alleging that the\nProgram itself (excluding combinations of the Program with other software\nor hardware) infringes such Recipient's patent(s), then such Recipient's\nrights granted under Section 2(b) shall terminate as of the date such\nlitigation is filed.\n\nAll Recipient's rights under this Agreement shall terminate if it\nfails to comply with any of the material terms or conditions of this\nAgreement and does not cure such failure in a reasonable period of\ntime after becoming aware of such noncompliance. If all Recipient's\nrights under this Agreement terminate, Recipient agrees to cease use\nand distribution of the Program as soon as reasonably practicable.\nHowever, Recipient's obligations under this Agreement and any licenses\ngranted by Recipient relating to the Program shall continue and survive.\n\nEveryone is permitted to copy and distribute copies of this Agreement,\nbut in order to avoid inconsistency the Agreement is copyrighted and\nmay only be modified in the following manner. The Agreement Steward\nreserves the right to publish new versions (including revisions) of\nthis Agreement from time to time. No one other than the Agreement\nSteward has the right to modify this Agreement. The Eclipse Foundation\nis the initial Agreement Steward. The Eclipse Foundation may assign the\nresponsibility to serve as the Agreement Steward to a suitable separate\nentity. Each new version of the Agreement will be given a distinguishing\nversion number. The Program (including Contributions) may always be\nDistributed subject to the version of the Agreement under which it was\nreceived. In addition, after a new version of the Agreement is published,\nContributor may elect to Distribute the Program (including its\nContributions) under the new version.\n\nExcept as expressly stated in Sections 2(a) and 2(b) above, Recipient\nreceives no rights or licenses to the intellectual property of any\nContributor under this Agreement, whether expressly, by implication,\nestoppel or otherwise. All rights in the Program not expressly granted\nunder this Agreement are reserved. Nothing in this Agreement is intended\nto be enforceable by any entity that is not a Contributor or Recipient.\nNo third-party beneficiary rights are created under this Agreement.\n\nExhibit A - Form of Secondary Licenses Notice\n\n\"This Source Code may also be made available under the following \nSecondary Licenses when the conditions for such availability set forth \nin the Eclipse Public License, v. 2.0 are satisfied: {name license(s),\nversion(s), and exceptions or additional permissions here}.\"\n\n  Simply including a copy of this Agreement, including this Exhibit A\n  is not sufficient to license the Source Code under Secondary Licenses.\n\n  If it is not possible or desirable to put the notice in a particular\n  file, then You may include the notice in a location (such as a LICENSE\n  file in a relevant directory) where a recipient would be likely to\n  look for such a notice.\n\n  You may add additional accurate notices of copyright ownership."
  },
  {
    "path": "README.md",
    "content": "# sieppari [![cljdoc badge](https://cljdoc.org/badge/metosin/sieppari)](https://cljdoc.org/d/metosin/sieppari/CURRENT)\n\n\nSmall, fast, and complete interceptor library for Clojure/Script with built-in support\nfor common async libraries.\n\n> Noun\n> **Siepata (Intercept)**\n>\n>   sieppari, _someone or something that intercepts_\n\n## What it does\n\nInterceptors, like in [Pedestal](http://pedestal.io/reference/interceptors), but\nwith minimal implementation and optimal performance.\n\nThe core _Sieppari_ depends on Clojure and nothing else.\n\nIf you are new to interceptors, check the\n[Pedestal Interceptors documentation](http://pedestal.io/pedestal/0.7/reference/interceptors.html).\nSieppari's `sieppari.core/execute` follows a `:request` / `:response` pattern. For\nPedestal-like behavior, use `sieppari.core/execute-context`.\n\n## First example\n\n```clj\n(ns example.simple\n  (:require [sieppari.core :as s]))\n\n;; interceptor, in enter update value in `[:request :x]` with `inc`\n(def inc-x-interceptor\n  {:enter (fn [ctx] (update-in ctx [:request :x] inc))})\n\n;; handler, take `:x` from request, apply `inc`, and return an map with `:y`\n(defn handler [request]\n  {:y (inc (:x request))})\n\n(s/execute\n  [inc-x-interceptor handler]\n  {:x 40})\n;=> {:y 42}\n```\n\n## Async\n\nAny step in the execution pipeline (`:enter`, `:leave`, `:error`) can return either a context map (synchronous execution) or an instance of [`AsyncContext`](https://github.com/metosin/sieppari/blob/develop/src/sieppari/async.cljc) - indicating asynchronous execution.\n\nBy default, clojure deferrables, `java.util.concurrent.CompletionStage` and `js/promise` satisfy the `AsyncContext` protocol.\n\nUsing `s/execute` with async steps will block:\n\n```clj\n;; async interceptor, in enter double value of `[:response :y]`:\n(def multiply-y-interceptor\n  {:leave (fn [ctx]\n            (future\n              (Thread/sleep 1000)\n              (update-in ctx [:response :y] * 2)))})\n\n\n(s/execute\n  [inc-x-interceptor multiply-y-interceptor handler]\n  {:x 40})\n; ... 1 second later:\n;=> {:y 84}\n```\n\nUsing non-blocking version of `s/execute`:\n\n```clj\n(s/execute\n  [inc-x-interceptor multiply-y-interceptor handler]\n  {:x 40}\n  (partial println \"SUCCESS:\")\n  (partial println \"FAILURE:\"))\n; => nil\n; prints \"SUCCESS: {:y 84}\" 1sec later\n```\n\nBlocking on async computation:\n\n```clj\n(let [respond (promise)\n      raise (promise)]\n  (s/execute\n    [inc-x-interceptor multiply-y-interceptor handler]\n    {:x 40}\n    respond\n    raise) ; returns nil immediately\n\n  (deref respond 2000 :timeout))\n; ... 1 second later:\n;=> {:y 84}\n```\n\nAny step can return a `java.util.concurrent.CompletionStage` or `js/promise`, Sieppari works oob with libraries like [Promesa](http://funcool.github.io/promesa/latest):\n\n```clj\n;; [funcool/promesa \"5.1.0\"]`\n(require '[promesa.core :as p])\n\n(def chain\n  [{:enter #(update-in % [:request :x] inc)}               ;; 1\n   {:leave #(p/promise (update-in % [:response :x] / 10))} ;; 4\n   {:enter #(p/delay 1000 %)}                              ;; 2\n   identity])                                              ;; 3\n\n;; blocking\n(s/execute chain {:x 40})\n; => {:x 41/10} after after 1sec\n\n;; non-blocking\n(s/execute\n  chain\n  {:x 40}\n  (partial println \"SUCCESS:\")\n  (partial println \"FAILURE:\"))\n; => nil\n;; prints \"SUCCESS: {:x 41/10}\" after 1sec\n```\n\n## External Async Libraries\n\nTo add a support for one of the supported external async libraries, just add a dependency to them and `require` the\nrespective Sieppari namespace. Currently supported async libraries are:\n\n* [core.async](https://github.com/clojure/core.async) - `sieppari.async.core-async`, clj & cljs\n* [Manifold](https://github.com/ztellman/manifold) - `sieppari.async.manifold` clj\n\nTo extend Sieppari async support to other libraries, just extend the `AsyncContext` protocol.\n\n## core.async\n\nRequires dependency to `[org.clojure/core.async \"0.4.474\"]` or higher.\n\n```clj\n(require '[clojure.core.async :as a])\n\n(defn multiply-x-interceptor [n]\n  {:enter (fn [ctx]\n            (a/go (update-in ctx [:request :x] * n)))})\n\n(s/execute\n  [inc-x-interceptor (multiply-x-interceptor 10) handler]\n  {:x 40})\n;=> {:y 411}\n```\n\n## manifold\n\nRequires dependency to `[manifold \"0.1.8\"]` or higher.\n\n```clj\n(require '[manifold.deferred :as d])\n\n(defn minus-x-interceptor [n]\n  {:enter (fn [ctx]\n            (d/success-deferred (update-in ctx [:request :x] - n)))})\n\n(s/execute\n  [inc-x-interceptor (minus-x-interceptor 10) handler]\n  {:x 40})\n;=> {:y 31}\n```\n\n# Performance\n\n_Sieppari_ aims for minimal functionality and can therefore be\nquite fast. Complete example to test performance is\n[included](https://github.com/metosin/sieppari/blob/develop/examples/example/perf_testing.clj).\n\n## Silly numbers\n\nExecuting a chain of 10 interceptors, which have `:enter` of `clojure.core/identity`.\n\n* **sync**: all steps return the ctx\n* **promesa**: all steps return the ctx in an `promesa.core/promise`\n* **core.async**: all step return the ctx in a `core.async` channel\n* **manifold**: all step return the ctx in a `manifold.deferred.Deferred`\n\nAll numbers are execution time lower quantile (not testing the goodness of the async libraries\n, just the execution overhead sippari interceptors adds)\n\n| Executor          | sync   | promesa | core.async | manifold |\n| ----------------- | -------|---------|------------|----------|\n| Pedestal          | 8.2µs  |  -      | 92µs       | -        |\n| Sieppari          | 1.2µs  |  4.0µs  | 70µs       | 110µs    |\n| Middleware (comp) | 0.1µs  |  -      | -          | -        |\n\n* MacBook Pro (Retina, 15-inch, Mid 2015), 2.5 GHz Intel Core i7, 16 MB RAM\n* Java(TM) SE Runtime Environment (build 1.8.0_151-b12)\n* Clojure 1.9.0\n\n**NOTE**: running async flows without interceptors is still much faster,\ne.g. synchronous `manifold` chain is much faster than via interceptors.\n\n**NOTE**: Goal is to have a Java-backed and optimized chain compiler into Sieppari,\ninitial tests show it will be near the perf of middleware chain / `comp`.\n\n# Differences to Pedestal\n\n## Execution\n\n* `io.pedestal.interceptor.chain/execute` executes _Contexts_\n* `sieppari.core/execute` executes _Requests_ (which are internally wrapped inside a _Context_ for interceptors)\n\n## Errors\n\n* In _Pedestal_ the `error` handler takes two arguments, the `ctx` and the exception.\n* In _Sieppari_ the `error` handlers takes just one argument, the `ctx`, and the exception is in the `ctx` under the key `:error`.\n* In _Pedestal_ the `error` handler resolves the exception by returning the `ctx`, and continues the **error** stage by re-throwing the exception.\n* In _Sieppari_ the `error` handler resolves the exception by returning the `ctx` with the `:error` removed. To continue in the **error**  stage, just return the `ctx` with the exception still at `:error`.\n*  In _Pedestal_ the exception are wrapped in other exceptions.\n* In _Sieppari_ exceptions are not wrapped.\n* _Pedestal_ interception execution catches `java.lang.Throwable` for error processing. _Sieppari_ catches `java.lang.Exception`. This means that things like out of memory or class loader failures are not captured by _Sieppari_.\n\n## Async\n\n* _Pedestal_ transfers thread local bindings from call-site into async interceptors.\n* _Sieppari_ does not support this.\n\n# Thanks\n\n* Original idea from [Pedestal Interceptors](https://github.com/pedestal/pedestal/tree/master/interceptor).\n\n## License\n\nCopyright &copy; 2018-2020 [Metosin Oy](https://www.metosin.fi/)\n\nDistributed under the Eclipse Public License 2.0.\n\n"
  },
  {
    "path": "deps.edn",
    "content": ";; Do *not* add deps, aliases etc. here. Use the existing project.clj instead\n;; this is for the sole purpose of exposing sieppari's git coords for tools.deps projects\n\n{:paths [\"src\"]}\n"
  },
  {
    "path": "dev/lumo_runner.cljs",
    "content": "(ns sieppari.lumo-runner\n  (:require [cljs.test :as test :refer-macros [run-tests]]\n            [lumo.core :as lumo]\n            sieppari.context-test\n            sieppari.queue-test\n            sieppari.interceptor-test\n            sieppari.core-execute-test\n            ;; sieppari.promesa-test       ;; not self-host compatible yet\n            ;; sieppari.async.promesa-test\n            sieppari.native-promise-test\n            sieppari.core-async-test\n            sieppari.async.core-async-test))\n\n(enable-console-print!)\n\n(defmethod test/report [:cljs.test/default :end-run-tests] [m]\n  (when-not (test/successful? m)\n    (lumo/exit 1)))\n\n(run-tests\n 'sieppari.context-test\n 'sieppari.queue-test\n 'sieppari.interceptor-test\n 'sieppari.core-execute-test\n 'sieppari.native-promise-test\n 'sieppari.core-async-test\n 'sieppari.async.core-async-test)\n"
  },
  {
    "path": "dev/user.clj",
    "content": "(ns user\n  (:require [clojure.tools.namespace.repl :as repl]\n            [kaocha.repl :as kaocha]))\n\n(def reset repl/refresh)\n(def start (constantly :ok))\n(def stop (constantly :ok))\n"
  },
  {
    "path": "examples/example/perf_testing.clj",
    "content": "(ns example.perf-testing\n  (:require [criterium.core :as criterium]\n            [sieppari.core :as s]\n            [sieppari.queue :as sq]\n            [sieppari.async.core-async]\n            [io.pedestal.interceptor :as pi]\n            [io.pedestal.interceptor.chain :as pc]\n            [manifold.deferred :as d]\n            [promesa.core :as p]\n            [clojure.core.async :as a]))\n\n(set! *warn-on-reflection* true)\n\n(defn raw-title [color s]\n  (println (str color (apply str (repeat (count s) \"#\")) \"\\u001B[0m\"))\n  (println (str color s \"\\u001B[0m\"))\n  (println (str color (apply str (repeat (count s) \"#\")) \"\\u001B[0m\")))\n\n(def title (partial raw-title \"\\u001B[35m\"))\n(def suite (partial raw-title \"\\u001B[32m\"))\n\n(defmacro bench! [name & body]\n  `(do\n     (title ~name)\n     (assert (= ~@body {}))\n     (let [{[lower#] :lower-q :as res#} (criterium/quick-benchmark (do ~@body) nil)]\n       (println \"\\u001B[32m\\n\" (format \"%.2fµs\" (* 1000000 lower#)) \"\\u001B[0m\")\n       (println)\n       (criterium/report-result res#))\n     (println)))\n\n(defn make-capture-result-interceptor [p]\n  (pi/interceptor\n    {:leave (fn [ctx]\n              (deliver p (:response ctx))\n              ctx)}))\n\n(comment\n\n  ; 2.7µs\n  ; 0.5µs (chain')\n  (bench!\n    \"manifold: identity\"\n    @(d/chain'\n       {}\n       identity\n       identity\n       identity\n       identity\n       identity\n       identity\n       identity\n       identity\n       identity\n       identity))\n\n  ; 73µs\n  ; 82µs (chain')\n  (bench!\n    \"manifold: future\"\n    @(d/chain'\n       {}\n       d/future\n       d/future\n       d/future\n       d/future\n       d/future\n       d/future\n       d/future\n       d/future\n       d/future\n       d/future))\n\n  ; 3.5µs\n  ; 1.5µs (chain')\n  (bench!\n    \"manifold: success-deferred\"\n    @(d/chain'\n       {}\n       d/success-deferred\n       d/success-deferred\n       d/success-deferred\n       d/success-deferred\n       d/success-deferred\n       d/success-deferred\n       d/success-deferred\n       d/success-deferred\n       d/success-deferred\n       success-deferred)))\n\n(def sync-interceptor {:enter identity})\n(def async-interceptor {:enter #(a/go %)})\n(def deferred-interceptor {:enter d/success-deferred})\n(def promesa-interceptor {:enter p/promise})\n(def future-interceptor {:enter #(future %)})\n(def delay-interceptor {:enter #(delay %)})\n\n(defn create-s-chain [n i]\n  (sq/into-queue (concat (repeat n i) [identity])))\n\n(defn create-s-mixed-chain [n i]\n  (sq/into-queue (concat (repeat (dec n) sync-interceptor) [i identity])))\n\n(defn run-simple-perf-test [n]\n  (let [sync-interceptors (concat (repeat n sync-interceptor) [identity])\n        async-interceptors (concat (repeat n async-interceptor) [identity])\n\n        p-context {:request {}}\n        p-sync-chain (mapv pi/interceptor sync-interceptors)\n        p-async-chain (map pi/interceptor async-interceptors)\n\n        s-sync-chain (create-s-chain n sync-interceptor)\n        s-async-chain (create-s-chain n async-interceptor)\n\n        s-deferred-chain (create-s-chain n deferred-interceptor)\n        s-future-chain (create-s-chain n future-interceptor)\n        s-delay-chain (create-s-chain n delay-interceptor)\n        s-promesa-chain (create-s-chain n promesa-interceptor)]\n\n    (suite (str \"queue of \" n))\n\n    ;; 8.2µs\n    ;; 8.2µs (context-api)\n    (bench!\n      \"pedestal: sync\"\n      (->> p-sync-chain\n           (pc/enqueue p-context)\n           (pc/execute)\n           :response))\n\n    ;; 99µs\n    ;; 92µs (context-api)\n    (bench!\n      \"pedestal: core.async\"\n      (let [p (promise)]\n        (->> (cons (make-capture-result-interceptor p) p-async-chain)\n             (pc/enqueue p-context)\n             (pc/execute))\n        @p))\n\n    ;; 1.3µs\n    ;; 2.1µs\n    ;; 1.2µs (context-api)\n    (bench!\n      \"sieppari: sync (sync)\"\n      (s/execute s-sync-chain {}))\n\n    ;; 1.3µs\n    ;; 2,5µs\n    ;; 1.2µs (context-api)\n    (bench!\n      \"sieppari: sync (async)\"\n      (let [p (promise)]\n        (s/execute s-sync-chain {} p identity)\n        @p))\n\n    ;; 61µs\n    ;; 69µs\n    ;; 66µs (context-api)\n    (bench!\n      \"sieppari: core.async (sync)\"\n      (s/execute s-async-chain {}))\n\n    ;; 60µs\n    ;; 65µs\n    ;; 70µs (context-api)\n    (bench!\n      \"sieppari: core.async (async)\"\n      (let [p (promise)]\n        (s/execute s-async-chain {} p identity)\n        @p))\n\n    ;; 140µs\n    ;; 140µs (context-api)\n    (bench!\n      \"sieppari: future (async)\"\n      (let [p (promise)]\n        (s/execute s-future-chain {} p identity)\n        @p))\n\n    ;; 84µs\n    ;; 100µs\n    ;; 110µs (context-api)\n    (bench!\n      \"sieppari: delay (async)\"\n      (let [p (promise)]\n        (s/execute s-delay-chain {} p identity)\n        @p))\n\n    ;; 84µs\n    ;; 62µs (chain'-)\n    ;; 89µs\n    ;; 90µs (context-api)\n    (bench!\n      \"sieppari: deferred (sync)\"\n      (s/execute s-deferred-chain {}))\n\n    ;; 84µs\n    ;; 84µs (chain'-)\n    ;; 150µs\n    ;; 110µs (context-api)\n    (bench!\n      \"sieppari: deferred (async)\"\n      (let [p (promise)]\n        (s/execute s-deferred-chain {} p identity)\n        @p))\n\n    ;; 36µs\n    ;; 3.8µs\n    ;; 5.4µs\n    ;; 3.8µs (context-api)\n    (bench!\n      \"sieppari: promesa (sync)\"\n      (s/execute s-promesa-chain {}))\n\n    ;; 38µs\n    ;; 4.0µs\n    ;; 5.3µs\n    ;; 4.0µs (context-api)\n    (bench!\n      \"sieppari: promesa (async)\"\n      (let [p (promise)]\n        (s/execute s-promesa-chain {} p identity)\n        @p))))\n\n(defn one-async-in-sync-pipeline-test [n]\n\n  (doseq [[name chain] [[(str \"homogeneous queue of \" n) create-s-chain]\n                        [(str \"queue of \" (dec n) \" sync + 1 async step\") create-s-mixed-chain]]\n          :let [_ (suite name)]\n          [name interceptor] [[\"identity\" sync-interceptor]\n                              #_[\"deferred\" deferred-interceptor]\n                              #_[\"core.async\" async-interceptor]\n                              [\"promesa\" promesa-interceptor]]]\n\n    (let [interceptors (chain n interceptor)]\n      (bench!\n        name\n        (let [p (promise)]\n          (s/execute interceptors {} p identity)\n          @p)))\n\n    ;; 1.8µs => 4.6µs => 1.8µs => 1.3µs\n    ;; 1.7µs => 4.6µs => 1.8µs => 1.4µs\n    \"identity\"\n\n    ;; 93µs => 100µs\n    ;; 20µs => 18µs\n    \"deferred\"\n\n    ;; 54µs => 73µs\n    ;; 20µs => 17µs\n    \"core.async\"\n\n    ;; 40µs => 4.0µs => 4.4µs => 3.9µs => 3.3µs\n    ;; 19µs => 2.5µs => 5.0µs => 2.0µs => 1.8µs\n    \"promesa\"))\n\n(defn middleware-comp [n]\n  (let [chain (apply comp (repeat n identity))]\n\n    ;; 105ns\n    (bench!\n      \"comp\"\n      (chain {}))))\n\n(defn pedestal-one-async [n]\n  (let [ctx {:request {}}\n        chain (conj (conj (vec (repeat (dec n) (pi/interceptor {:enter identity}))) (pi/interceptor {:enter #(a/go %)})) (pi/interceptor identity))]\n\n    ;; 50µs\n    (bench!\n      \"pedestal 9 sync + 1 async\"\n      (let [p (promise)]\n        (->> (cons (make-capture-result-interceptor p) chain)\n             (pc/enqueue ctx)\n             (pc/execute))\n        @p))))\n\n(defn -main [& _]\n  (run-simple-perf-test 10)\n  (one-async-in-sync-pipeline-test 10)\n  (middleware-comp 10)\n  (pedestal-one-async 10))\n\n(comment\n  (run-simple-perf-test 10)\n  (one-async-in-sync-pipeline-test 10)\n  (middleware-comp 10)\n  (pedestal-one-async 10)\n\n  (do\n    (require '[clj-async-profiler.core :as prof])\n    (prof/serve-files 8080))\n\n  (time\n    (prof/profile\n      (let [interceptors (create-s-chain 10 identity)]\n        (dotimes [_ 3000000]\n          (let [p (promise)]\n            (s/execute interceptors {} p identity)\n            @p))))))\n"
  },
  {
    "path": "examples/example/simple.cljc",
    "content": "(ns example.simple\n  (:require [sieppari.core :as s]))\n\n(def times-2-interceptor\n  {:enter (fn [ctx] (update-in ctx [:request :x] * 2))})\n\n(def inc-interceptor\n  {:enter (fn [ctx] (update-in ctx [:request :x] inc))})\n\n;; Simple handler, take `:x` from request, apply `inc`, and\n;; return an map with `:y`.\n\n(defn handler [request]\n  {:y (inc (:x request))})\n\n(def chain [times-2-interceptor\n            inc-interceptor\n            handler])\n\n#?(:clj (s/execute chain {:x 20})\n   :cljs (s/execute chain {:x 20} prn prn))\n;=> {:y 42}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"sieppari\",\n  \"description\": \"Small, fast, and complete interceptor library.\",\n  \"devDependencies\": {\n    \"ws\": \"^7.5.10\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/metosin/sieppari.git\"\n  },\n  \"license\": \"EPL-2.0\",\n  \"bugs\": {\n    \"url\": \"https://github.com/metosin/sieppari/issues\"\n  },\n  \"homepage\": \"https://github.com/metosin/sieppari#readme\"\n}\n"
  },
  {
    "path": "project.clj",
    "content": "(defproject metosin/sieppari \"0.0.0-alpha13\"\n  :description \"Small, fast, and complete interceptor library.\"\n  :url \"https://github.com/metosin/sieppari\"\n  :license {:name \"Eclipse Public License\", :url \"https://www.eclipse.org/legal/epl-2.0/\"}\n  :deploy-repositories [[\"releases\" :clojars]]\n  :lein-release {:deploy-via :clojars}\n\n  :dependencies []\n  :test-paths [\"test/clj\" \"test/cljs\" \"test/cljc\"]\n\n  :profiles {:dev {:source-paths [\"dev\"]\n                   :dependencies [[org.clojure/clojure \"1.10.1\" :scope \"provided\"]\n                                  [org.clojure/clojurescript \"1.10.758\"]\n                                  ;; Add-ons:\n                                  [org.clojure/core.async \"1.2.603\"]\n                                  [manifold \"0.1.8\"]\n                                  [funcool/promesa \"5.1.0\"]\n                                  ;; Testing:\n                                  [metosin/testit \"0.4.0\"]\n                                  [lambdaisland/kaocha \"1.0.632\"]\n                                  [lambdaisland/kaocha-cljs \"0.0-71\"]\n                                  ;; Dev:\n                                  [org.clojure/tools.namespace \"1.0.0\"]\n                                  ;; Perf testing:\n                                  [criterium \"0.4.5\"]\n                                  [com.clojure-goes-fast/clj-async-profiler \"0.5.0-SNAPSHOT\"]\n                                  [io.pedestal/pedestal.interceptor \"0.5.7\"]\n                                  [org.slf4j/slf4j-nop \"1.7.30\"]]}\n\n             ;; needed because of https://github.com/lambdaisland/kaocha-cljs#known-issues\n             :test-cljs {:source-paths [\"test/cljc\" \"test/cljs\"]}\n             :examples {:source-paths [\"examples\"]}\n             :perf {:jvm-opts ^:replace [\"-server\" \"-Xms4096m\" \"-Xmx4096m\" \"-Dclojure.compiler.direct-linking=true\"]}}\n\n  :aliases {\"kaocha\" [\"with-profile\" \"+dev-deps,+test-cljs\" \"run\" \"-m\" \"kaocha.runner\" \"--reporter\" \"kaocha.report/documentation\"]\n            \"perf\" [\"with-profile\" \"default,dev,examples,perf\"]\n            \"perf-test\" [\"perf\" \"run\" \"-m\" \"example.perf-testing\"]})\n"
  },
  {
    "path": "scripts/lein-modules",
    "content": "#!/bin/bash\n\nset -e\n\nfor ext in sieppari.core \\\n           sieppari.async.core-async \\\n           sieppari.async.deref \\\n           sieppari;\ndo\n  ( cd modules/$ext && lein \"$@\"; )\ndone\n"
  },
  {
    "path": "scripts/test-self-host",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\ncp=\"src:test/cljs:test/cljc:$(clojure -Srepro -A:self-host -Spath)\"\n\nlumo -K -c \"$cp\" dev/lumo_runner.cljs\n"
  },
  {
    "path": "src/sieppari/async/core_async.cljc",
    "content": "(ns sieppari.async.core-async\n  (:require [sieppari.async :as sa]\n            [sieppari.util :refer [exception?]]\n            [clojure.core.async :as cca\n             #?@(:clj  [:refer [go <! <!!]]\n                 :cljs [:refer-macros [go]])]))\n\n(extend-protocol sa/AsyncContext\n  #?(:clj  clojure.core.async.impl.protocols.Channel\n     :cljs cljs.core.async.impl.channels/ManyToManyChannel)\n  (async? [_] true)\n  (continue [c f] (go (f (cca/<! c))))\n  (catch [c f] (go (let [c (cca/<! c)]\n                     (if (exception? c) (f c) c))))\n  #?(:clj (await [c] (<!! c))))\n"
  },
  {
    "path": "src/sieppari/async/manifold.clj",
    "content": "(ns sieppari.async.manifold\n  (:require [sieppari.async :as sa]\n            [manifold.deferred :as d]))\n\n(extend-protocol sa/AsyncContext\n  manifold.deferred.Deferred\n  (async? [_] true)\n  (continue [d f] (d/chain'- nil d f))\n  (catch [d f] (d/catch' d f))\n  (await [d] (deref d))\n\n  manifold.deferred.ErrorDeferred\n  (async? [_] true)\n  (continue [d f] (d/chain'- nil d f))\n  (catch [d f] (d/catch' d f))\n  (await [d] (deref d)))\n"
  },
  {
    "path": "src/sieppari/async.cljc",
    "content": "(ns sieppari.async\n  #?(:clj (:refer-clojure :exclude [await]))\n  (:require [sieppari.util :refer [exception?]])\n  #?(:clj (:import java.util.concurrent.CompletionStage\n                   java.util.concurrent.CompletionException\n                   java.util.function.Function)))\n\n(defprotocol AsyncContext\n  (async? [t])\n  (continue [t f])\n  (catch [c f])\n  #?(:clj (await [t])))\n\n#?(:clj\n   (deftype FunctionWrapper [f]\n     Function\n     (apply [_ v]\n       (f v))))\n\n#?(:clj\n   (extend-protocol AsyncContext\n     Object\n     (async? [_] false)\n     (continue [t f] (f t))\n     (await [t] t)))\n\n#?(:cljs\n   (extend-protocol AsyncContext\n     default\n     (async? [_] false)\n     (continue [t f] (f t))))\n\n#?(:clj\n   (extend-protocol AsyncContext\n     clojure.lang.IDeref\n     (async? [_] true)\n     (continue [c f] (future (f @c)))\n     (catch [c f] (future (let [c @c]\n                            (if (exception? c) (f c) c))))\n     (await [c] @c)))\n\n#?(:clj\n   (extend-protocol AsyncContext\n     CompletionStage\n     (async? [_] true)\n     (continue [this f]\n       (.thenApply ^CompletionStage this\n                   ^Function (->FunctionWrapper f)))\n\n     (catch [this f]\n       (letfn [(handler [e]\n                  (if (instance? CompletionException e)\n                   (f (.getCause ^Exception e))\n                   (f e)))]\n         (.exceptionally ^CompletionStage this\n                         ^Function (->FunctionWrapper handler))))\n\n     (await [this]\n       (deref this))))\n\n#?(:cljs\n   (extend-protocol AsyncContext\n     js/Promise\n     (async? [_] true)\n     (continue [t f] (.then t f))\n     (catch [t f] (.catch t f))))\n"
  },
  {
    "path": "src/sieppari/context.cljc",
    "content": "(ns sieppari.context\n  (:require [sieppari.queue :as q]))\n\n(defprotocol Context\n  (context? [this]))\n\n(extend-protocol Context\n  #?(:clj  clojure.lang.PersistentHashMap\n     :cljs cljs.core.PersistentHashMap)\n  (context? [_] true)\n\n  #?(:clj  clojure.lang.PersistentArrayMap\n     :cljs cljs.core.PersistentArrayMap)\n  (context? [_] true))\n\n#?(:clj\n   (extend-protocol Context\n     Object\n     (context? [_] false)))\n\n#?(:cljs\n   (extend-protocol Context\n     default\n     (context? [_] false)))\n\n(defn terminate\n  \"Removes all remaining interceptors from context's execution queue.\n  This effectively short-circuits execution of Interceptors' :enter\n  functions and begins executing the :leave functions.\n  Two arity version allows setting the response at the same call.\"\n  ([ctx]\n   (assoc ctx :queue q/empty-queue))\n  ([ctx response]\n   (-> ctx\n       (assoc :queue q/empty-queue)\n       (assoc :response response))))\n\n(defn inject\n  \"Adds interceptor or seq of interceptors to the head of context's execution queue. Creates\n  the queue if necessary. Returns updated context.\"\n  [ctx interceptor-or-interceptors]\n  (let [interceptors (if (sequential? interceptor-or-interceptors)\n                       interceptor-or-interceptors\n                       (cons interceptor-or-interceptors nil))]\n    (assoc ctx :queue (q/into-queue (concat interceptors (:queue ctx))))))\n\n; TODO: figure out how enqueue should work? Should enqueue add interceptors just\n#_(defn enqueue\n    \"Adds interceptor or seq of interceptors to the end of context's execution queue. Creates\n    the queue if necessary. Returns updated context.\"\n    [ctx interceptor-or-interceptors]\n    (let [interceptors (into-interceptors (if (sequential? interceptor-or-interceptors)\n                                            interceptor-or-interceptors\n                                            [interceptor-or-interceptors]))]\n      (update ctx :queue (fnil into PersistentQueue/EMPTY) interceptors)))\n"
  },
  {
    "path": "src/sieppari/core.cljc",
    "content": "(ns sieppari.core\n  #?(:cljs (:refer-clojure :exclude [iter]))\n  (:require [sieppari.queue :as q]\n            [sieppari.async :as a]\n            [sieppari.context :as c]\n            #?(:cljs [goog.iter :as iter]))\n  #?(:clj (:import (java.util Iterator))))\n\n(defrecord Context [error queue stack]\n  c/Context\n  (context? [_] true))\n\n(defrecord RequestResponseContext [request response error queue stack]\n  c/Context\n  (context? [_] true))\n\n(defn- -try [ctx f]\n  (if f\n    (try\n      (let [ctx* (f ctx)]\n        (if (a/async? ctx*)\n          (a/catch ctx* (fn [e] (assoc ctx :error e)))\n          ctx*))\n      (catch #?(:clj Exception :cljs :default) e\n        (assoc ctx :error e)))\n    ctx))\n\n(defn- -iter [v]\n  #?(:clj  (clojure.lang.RT/iter v)\n     :cljs (cljs.core/iter v)))\n\n(defn- -invalid-context-type! [ctx stage]\n  (throw\n    (ex-info\n      (str \"Unsupported Context on \" stage\" - \" ctx)\n      {:ctx ctx})))\n\n(defn- leave [ctx]\n  (cond\n    (a/async? ctx) (a/continue ctx leave)\n    (c/context? ctx) (let [^Iterator it (:stack ctx)]\n                       (if (.hasNext it)\n                         (let [stage (if (:error ctx) :error :leave)\n                               f (-> it .next stage)]\n                           (recur (-try ctx f)))\n                         ctx))\n    :else (-invalid-context-type! ctx :leave)))\n\n(defn- enter [ctx]\n  (cond\n    (a/async? ctx) (a/continue ctx enter)\n    (c/context? ctx) (let [queue (:queue ctx)\n                           stack (:stack ctx)\n                           interceptor (peek queue)]\n                       (if (or (not interceptor) (:error ctx))\n                         (assoc ctx :stack (-iter stack))\n                         (recur (-> ctx\n                                    (assoc :queue (pop queue))\n                                    (assoc :stack #?(:clj  (conj stack interceptor)\n                                                     :cljs (doto (or stack (array))\n                                                             (.unshift interceptor))))\n                                    (-try (:enter interceptor))))))\n    :else (-invalid-context-type! ctx :enter)))\n\n#?(:clj\n   (defn- await-result [ctx get-result]\n     (if (a/async? ctx)\n       (recur (a/await ctx) get-result)\n       (if-let [error (:error ctx)]\n         (throw error)\n         (get-result ctx)))))\n\n(defn- deliver-result [ctx get-result on-complete on-error]\n  (if (a/async? ctx)\n    (a/continue ctx #(deliver-result % get-result on-complete on-error))\n    (let [error (:error ctx)\n          result (or error (get-result ctx))\n          callback (if error on-error on-complete)]\n      (callback result))))\n\n(defn- remove-context-keys [ctx]\n  (dissoc ctx :error :queue :stack))\n\n;;\n;; Public API:\n;;\n\n(defn execute-context\n  {:arglists\n   '([interceptors ctx]\n     [interceptors ctx on-complete on-error])}\n  ([interceptors ctx on-complete on-error]\n   (execute-context interceptors ctx on-complete on-error remove-context-keys))\n  ([interceptors ctx on-complete on-error get-result]\n   (if-let [queue (q/into-queue interceptors)]\n     (try\n       (-> (assoc ctx :queue queue)\n           (map->Context)\n           (enter)\n           (leave)\n           (deliver-result get-result on-complete on-error))\n       (catch #?(:clj Exception :cljs js/Error) e (on-error e)))\n     (on-complete nil))\n   nil)\n  #?(:clj\n     ([interceptors ctx]\n      (execute-context interceptors ctx remove-context-keys)))\n  #?(:clj\n     ([interceptors ctx get-result]\n      (when-let [queue (q/into-queue interceptors)]\n        (-> (assoc ctx :queue queue)\n            (map->Context)\n            (enter)\n            (leave)\n            (await-result get-result))))))\n\n(defn execute\n  {:arglists\n   '([interceptors request]\n     [interceptors request on-complete on-error])}\n  ([interceptors request on-complete on-error]\n   (if-let [queue (q/into-queue interceptors)]\n     (try\n       (-> (new RequestResponseContext request nil nil queue nil)\n           (enter)\n           (leave)\n           (deliver-result :response on-complete on-error))\n       (catch #?(:clj Exception :cljs js/Error) e (on-error e)))\n     (on-complete nil))\n   nil)\n  #?(:clj\n     ([interceptors request]\n      (when-let [queue (q/into-queue interceptors)]\n        (-> (new RequestResponseContext request nil nil queue nil)\n            (enter)\n            (leave)\n            (await-result :response))))))\n"
  },
  {
    "path": "src/sieppari/interceptor.cljc",
    "content": "(ns sieppari.interceptor\n  (:require [sieppari.async :as a]\n            [sieppari.util :refer [exception?]]))\n\n(defrecord Interceptor [name enter leave error])\n\n(defprotocol IntoInterceptor\n  (into-interceptor [t] \"Given a value, produce an Interceptor Record.\"))\n\n(defn- set-result [ctx response]\n  (if (and (some? response) (a/async? response))\n    (a/continue response (partial set-result ctx))\n    (assoc ctx\n      (if (exception? response) :error :response)\n      response)))\n\n(extend-protocol IntoInterceptor\n  ;; Map -> Interceptor:\n  #?(:clj  clojure.lang.IPersistentMap\n     :cljs cljs.core.PersistentHashMap)\n  (into-interceptor [interceptor-map]\n    (map->Interceptor interceptor-map))\n\n  #?(:cljs cljs.core.PersistentArrayMap)\n  (into-interceptor [interceptor-map]\n    (map->Interceptor interceptor-map))\n\n  ;; Function -> Handler interceptor:\n  #?(:clj  clojure.lang.Fn\n     :cljs function)\n  (into-interceptor [handler]\n    (into-interceptor {:enter (fn [ctx]\n                                (set-result ctx (handler (:request ctx))))}))\n\n  ;; Vector -> Interceptor, first element is a function to create\n  ;; the interceptor, rest are arguments for it:\n  #?(:clj  clojure.lang.IPersistentVector\n     :cljs cljs.core.PersistentVector)\n  (into-interceptor [t]\n    (into-interceptor (apply (first t) (rest t))))\n\n  ;; Interceptor -> Interceptor, nop:\n  Interceptor\n  (into-interceptor [t]\n    t)\n\n  ;; nil -> nil, nop:\n  nil\n  (into-interceptor [_]\n    nil))\n"
  },
  {
    "path": "src/sieppari/queue.cljc",
    "content": "(ns sieppari.queue\n  (:require [sieppari.interceptor :as i]))\n\n(defprotocol IntoQueue\n  (into-queue [t]))\n\n(def empty-queue\n  #?(:clj  clojure.lang.PersistentQueue/EMPTY\n     :cljs #queue []))\n\n(defn- into-queue*\n  [t]\n  (when (seq t)\n    (into empty-queue\n          (keep i/into-interceptor)\n          t)))\n\n#?(:clj\n   (extend-protocol IntoQueue\n     clojure.lang.PersistentQueue\n     (into-queue [t]\n       t)\n\n     clojure.lang.ISeq\n     (into-queue [t]\n       (into-queue* t))\n\n     clojure.lang.Seqable\n     (into-queue [t]\n       (into-queue* (seq t)))\n\n     nil\n     (into-queue [_])))\n\n#?(:cljs\n   (extend-protocol IntoQueue\n     cljs.core.PersistentQueue\n     (into-queue [t]\n       t)\n\n     cljs.core.List\n     (into-queue [t]\n       (into-queue* t))\n\n     cljs.core.LazySeq\n     (into-queue [t]\n       (into-queue* t))\n\n     cljs.core.PersistentVector\n     (into-queue [t]\n       (into-queue* t))\n\n     cljs.core.RSeq\n     (into-queue [t]\n       (into-queue* t))\n\n     cljs.core.EmptyList\n     (into-queue [_])\n\n     cljs.core.Cons\n     (into-queue [t]\n       (conj empty-queue t))\n\n     array\n     (into-queue [t]\n       (into-queue* t))\n\n     nil\n     (into-queue [_])))\n"
  },
  {
    "path": "src/sieppari/util.cljc",
    "content": "(ns sieppari.util)\n\n(defn exception? [e]\n  (instance? #?(:clj Exception :cljs js/Error) e))\n"
  },
  {
    "path": "test/clj/sieppari/async/manifold_test.clj",
    "content": "(ns sieppari.async.manifold-test\n  (:require [clojure.test :refer :all]\n            [testit.core :refer :all]\n            [sieppari.async :as as]\n            [sieppari.async.manifold]\n            [manifold.deferred :as d]))\n\n(deftest async?-test\n  (fact\n    (as/async? (d/deferred)) => true))\n\n(deftest continue-test\n  (let [p (promise)\n        d (d/deferred)]\n    (future\n      (d/success! d \"foo\"))\n    (as/continue d (partial deliver p))\n    (fact\n      @p =eventually=> \"foo\")))\n\n(deftest catch-test\n  (let [p (promise)\n        d (d/deferred)]\n    (future\n      (d/success! d \"foo\"))\n    (as/catch (as/continue d (partial deliver p))\n              (fn [_] (deliver p \"barf\")))\n    (fact\n      @p =eventually=> \"foo\"))\n\n  (let [p (promise)\n        d (d/deferred)]\n    (future\n      (d/error! d (Exception. \"fubar\")))\n    (as/catch d (fn [_] (deliver p \"foo\")))\n    (fact\n      @p =eventually=> \"foo\")))\n\n(deftest await-test\n  (fact\n    (as/await (let [d (d/deferred)]\n                (future\n                  (d/success! d \"foo\"))\n                d))\n    => \"foo\"))\n"
  },
  {
    "path": "test/clj/sieppari/core_async_test.clj",
    "content": "(ns sieppari.core-async-test\n  (:require [clojure.test :refer :all]\n            [testit.core :refer :all]\n            [sieppari.core :as sc]\n            [clojure.core.async :refer [go <! <!!]]))\n\n(defn make-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) ctx)\n   :leave (fn [ctx] (swap! log conj [:leave name]) ctx)\n   :error (fn [ctx] (swap! log conj [:error name]) ctx)})\n\n(defn make-async-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) (go ctx))\n   :leave (fn [ctx] (swap! log conj [:leave name]) (go ctx))\n   :error (fn [ctx] (swap! log conj [:error name]) (go ctx))})\n\n(defn make-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    request))\n\n(defn make-async-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    (go request)))\n\n(def request {:foo \"bar\"})\n(def error (ex-info \"oh no\" {}))\n\n(defn fail! [& _]\n  (throw (ex-info \"Should never be called\" {})))\n\n(deftest setup-sync-test-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest setup-async-test-test\n  (let [log (atom [])\n        response (-> [(make-async-logging-interceptor log :a)\n                      (make-async-logging-interceptor log :b)\n                      (make-async-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest execute-context-setup-async-test-test\n  (let [log (atom [])\n        response (-> [(make-async-logging-interceptor log :a)\n                      (make-async-logging-interceptor log :b)\n                      (make-async-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute-context {:request request}))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      (:response response) => request)))\n\n(deftest async-b-sync-execute-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-async-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-handler-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-stack-sync-execute-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      response => request)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:leave :c]\n                          [:leave :b]\n                          [:leave :a]])))\n\n(deftest async-stack-async-execute-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (make-async-logging-handler log)]\n        (sc/execute request (partial deliver response-p) fail!))\n    (fact\n      @response-p =eventually=> request)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:leave :c]\n                          [:leave :b]\n                          [:leave :a]])))\n\n(deftest async-execute-with-error-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (fn [_]\n           (swap! log conj [:handler])\n           (throw error))]\n        (sc/execute request fail! (partial deliver response-p)))\n    (fact\n      @response-p =eventually=> error)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:error :c]\n                          [:error :b]\n                          [:error :a]])))\n\n(deftest async-execute-rejection-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (fn [_]\n           (swap! log conj [:handler])\n           (go error))]\n        (sc/execute request fail! (partial deliver response-p)))\n    (fact\n      @response-p =eventually=> error)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:error :c]\n                          [:error :b]\n                          [:error :a]])))\n\n(deftest async-failing-handler-test\n  (let [log (atom [])]\n    (fact\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (go error))]\n          (sc/execute request))\n      =throws=> error)\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:error :c]\n               [:error :b]\n               [:error :a]])))\n\n(deftest async-failing-handler-b-fixes-test\n  (let [log (atom [])]\n    (fact\n      (-> [(make-logging-interceptor log :a)\n           (assoc (make-async-logging-interceptor log :b)\n             :error (fn [ctx]\n                      (swap! log conj [:error :b])\n                      (go\n                        (-> ctx\n                            (assoc :error nil)\n                            (assoc :response :fixed-by-b)))))\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (go error))]\n          (sc/execute request))\n      => :fixed-by-b)\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:error :c]\n               [:error :b]\n               [:leave :a]])))\n"
  },
  {
    "path": "test/clj/sieppari/core_execute_test.clj",
    "content": "(ns sieppari.core-execute-test\n  (:require [clojure.test :refer :all]\n            [testit.core :refer :all]\n            [sieppari.core :as s]\n            [sieppari.context :as sc]\n            [clojure.string :as str]))\n\n;;\n;; Following tests use a test-chain that has some interceptors\n;; that fail on each stage function (enter, leave, error). The\n;; idea is that the tests override the expected stage functions\n;; with test specific function. This ensures that no unexpected\n;; stage functions are called.\n;;\n\n; Make an interceptor with given name and set all stage functions\n; to report unexpected invocation. Tests should override expected\n; stages.\n\n(defn unexpected [name stage]\n  (fn [ctx]\n    (throw (ex-info \"unexpected invocation\"\n                    {:name name\n                     :stage stage\n                     :ctx ctx}))))\n\n(defn make-test-interceptor [name]\n  {:name name\n   :enter (unexpected name :enter)\n   :leave (unexpected name :leave)\n   :error (unexpected name :error)})\n\n; Test stack with three interceptors and a handler:\n\n(def test-chain [(make-test-interceptor :a)\n                 (make-test-interceptor :b)\n                 (make-test-interceptor :c)\n                 (unexpected :handler nil)])\n\n(def a-index 0)\n(def b-index 1)\n(def c-index 2)\n(def h-index 3)\n\n;; Helper: always throws an exception with specific marker\n;; in data part:\n\n(def error (ex-info \"oh no\" {::error-marker true}))\n\n(defn always-throw [ctx]\n  (throw error))\n\n;; Helper: return error handler function that ensures\n;; that `ctx` contains an exception caused by `always-throw`,\n;; clears the exception and sets response to given response:\n\n(defn handle-error [overwrite-context]\n  (fn [ctx]\n    (assert (-> ctx :error ex-data (= {::error-marker true})))\n    (-> ctx\n        (dissoc :error)\n        (merge overwrite-context))))\n\n(defn handle-error-response [response]\n  (fn [ctx]\n    (assert (not (some? (:response ctx))))\n    (assert (-> ctx :error ex-data (= {::error-marker true})))\n    (-> ctx\n        (dissoc :error)\n        (assoc :response response))))\n\n;;\n;; Tests:\n;;\n\n;;`execute-context` Tests\n\n(deftest execute-context-test\n  (fact \"enable all enter and leave stages, add `inc` interceptor\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] identity)\n        (assoc-in [h-index] {:enter (fn [ctx] (update ctx :data inc))})\n        (assoc-in [c-index :leave] identity)\n        (assoc-in [b-index :leave] identity)\n        (assoc-in [a-index :leave] identity)\n        (s/execute-context {:data 41}))\n    => {:data 42}))\n\n(deftest execute-context-enter-b-causes-exception-test\n  (fact \":b causes an exception\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] always-throw)\n        (assoc-in [b-index :error] identity)\n        (assoc-in [a-index :error] identity)\n        (s/execute-context {:data 41}))\n    =throws=> error))\n\n(deftest execute-context-enter-c-causes-exception-a-handles-test\n  (fact \":c enter causes an exception, :b sees error, :a handles\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] identity)\n        (assoc-in [a-index :error] (handle-error {:data :fixed-by-a}))\n        (s/execute-context {:data 41}))\n    => {:data :fixed-by-a}))\n\n(deftest execute-context-enter-c-causes-exception-b-handles-test\n  (fact \":c enter causes an exception, :b handles\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] (handle-error {:data :fixed-by-b}))\n        (assoc-in [a-index :leave] identity)\n        (s/execute-context {:data 41}))\n    => {:data :fixed-by-b}))\n\n(deftest execute-context-enter-b-sets-response-test\n  (fact \":b sets the response, no invocation of :c nor :handler\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] (fn [ctx] (sc/terminate\n                                               (assoc ctx :data :response-by-b))))\n        (assoc-in [b-index :leave] identity)\n        (assoc-in [a-index :leave] identity)\n        (s/execute-context {:data 41}))\n    => {:data :response-by-b}))\n\n(deftest execute-context-empty-interceptors-test\n  (facts \"interceptor chain can be empty\"\n    (s/execute-context [] {}) => nil))\n\n(defn make-logging-interceptor [name]\n  {:name name\n   :enter (fn [ctx]\n            (update ctx :request conj [:enter name]))\n   :leave (fn [ctx]\n            (update ctx :response conj [:leave name]))})\n\n(defn logging-handler [request]\n  (conj request [:handler]))\n\n(deftest execute-context-inject-interceptor-test\n  (fact \":b injects interceptor :x to chain, ensure the order is correct\"\n    (-> [(make-logging-interceptor :a)\n         {:enter (fn [ctx] (sc/inject ctx (make-logging-interceptor :x)))}\n         (make-logging-interceptor :c)\n         logging-handler]\n        (s/execute-context {:request []})\n        :response)\n    => [[:enter :a]\n        [:enter :x]\n        [:enter :c]\n        [:handler]\n        [:leave :c]\n        [:leave :x]\n        [:leave :a]]))\n\n\n;;`execute` Tests\n\n(deftest happy-case-test\n  (fact \"enable all enter and leave stages, use `inc` as handler\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] identity)\n        (assoc-in [h-index] inc)\n        (assoc-in [c-index :leave] identity)\n        (assoc-in [b-index :leave] identity)\n        (assoc-in [a-index :leave] identity)\n        (s/execute 41))\n    => 42))\n\n(deftest enter-b-causes-exception-test\n  (fact \":b causes an exception\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] always-throw)\n        (assoc-in [b-index :error] identity)\n        (assoc-in [a-index :error] identity)\n        (s/execute 41))\n    =throws=> error))\n\n(deftest enter-c-causes-exception-a-handles-test\n  (fact \":c enter causes an exception, :b sees error, :a handles\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] identity)\n        (assoc-in [a-index :error] (handle-error-response :fixed-by-a))\n        (s/execute 41))\n    => :fixed-by-a))\n\n(deftest enter-c-causes-exception-b-handles-test\n  (fact \":c enter causes an exception, :b handles\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] (handle-error-response :fixed-by-b))\n        (assoc-in [a-index :leave] identity)\n        (s/execute 41))\n    => :fixed-by-b))\n\n(deftest handler-causes-exception-b-handles-test\n  (fact\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] identity)\n        (assoc-in [h-index] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] (handle-error-response :fixed-by-b))\n        (assoc-in [a-index :leave] identity)\n        (s/execute 41))\n    => :fixed-by-b))\n\n(deftest enter-b-sets-response-test\n  (fact \":b sets the response, no invocation of :c nor :handler\"\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] (fn [ctx] (sc/terminate ctx :response-by-b)))\n        (assoc-in [b-index :leave] identity)\n        (assoc-in [a-index :leave] identity)\n        (s/execute 41))\n    => :response-by-b))\n\n(deftest nil-response-test\n  (fact \"nil response is allowed\"\n    (s/execute [(constantly nil)] {})\n    => nil))\n\n(deftest empty-interceptors-test\n  (facts \"interceptor chain can be empty\"\n    (s/execute [] {}) => nil\n    (s/execute nil {}) => nil))\n\n(defn make-logging-interceptor [name]\n  {:name name\n   :enter (fn [ctx] (update ctx :request conj [:enter name]))\n   :leave (fn [ctx] (update ctx :response conj [:leave name]))})\n\n(defn logging-handler [request]\n  (conj request [:handler]))\n\n(deftest inject-interceptor-test\n  (fact \":b injects interceptor :x to chain, ensure the order is correct\"\n    (-> [(make-logging-interceptor :a)\n         {:enter (fn [ctx] (sc/inject ctx (make-logging-interceptor :x)))}\n         (make-logging-interceptor :c)\n         logging-handler]\n        (s/execute []))\n    => [[:enter :a]\n        [:enter :x]\n        [:enter :c]\n        [:handler]\n        [:leave :c]\n        [:leave :x]\n        [:leave :a]]))\n\n; TODO: figure out how enqueue should work? Should enqueue add interceptors just\n; before the handler?\n#_(deftest enqueue-interceptor-test\n    (fact \":b enqueues interceptor :x to chain, ensure the order is correct\"\n      (-> [(make-logging-interceptor :a)\n           {:enter (fn [ctx] (sc/enqueue ctx (make-logging-interceptor :x)))}\n           (make-logging-interceptor :c)\n           logging-handler]\n          (sc/into-interceptors)\n          (s/execute []))\n      => [[:enter :a]\n          [:enter :c]\n          [:enter :x]\n          [:handler]\n          [:leave :x]\n          [:leave :c]\n          [:leave :a]]))\n\n(defrecord UnsupportedContext [])\n\n(defn invalid-context-class-exception? [e]\n  (-> e .getMessage (str/starts-with? \"Unsupported Context on :enter\")))\n\n(deftest invalid-context-test\n\n  (fact \"fails on sync\"\n    (s/execute [{:enter map->UnsupportedContext}] {:x 40})\n    =throws=> invalid-context-class-exception?)\n\n  (testing \"async\"\n    (let [on-error (promise)]\n      (s/execute [{:enter map->UnsupportedContext}] {:x 40} ::irrelevant on-error)\n\n      (fact \"responds failure\"\n        @on-error => invalid-context-class-exception?))))\n"
  },
  {
    "path": "test/clj/sieppari/core_test.clj",
    "content": "(ns sieppari.core-test\n  (:require [clojure.test :refer :all]\n            [testit.core :refer :all]\n            [sieppari.core :as s]\n            [sieppari.async.core-async]\n            [clojure.core.async :as a]))\n\n(def try-f #'s/-try)\n\n(deftest try-f-test\n  (fact\n    (try-f {} nil)\n    => {})\n  (fact\n    (try-f {} (fn [ctx] (assoc ctx :foo \"bar\")))\n    => {:foo \"bar\"})\n  (fact\n    (try-f {} (fn [_] (throw (ex-info \"oh no\" {}))))\n    =in=> {:error (ex-info? \"oh no\" {})})\n  (fact\n    @(try-f {} (fn [_] (future (ex-info \"oh no\" {}))))\n    =eventually-in=> {:error (ex-info? \"oh no\" {})}))\n\n(def await-result #'s/await-result)\n\n(def error (RuntimeException. \"kosh\"))\n\n(deftest wait-result-core-async-test\n  (facts \"response\"\n    (await-result {:response :ctx} :response) => :ctx\n    (await-result (a/go {:response :ctx}) :response) => :ctx\n    (await-result (a/go (a/go {:response :ctx})) :response) => :ctx)\n  (facts \"error\"\n    (await-result {:error error} :response) =throws=> error\n    (await-result (a/go {:error error}) :response) =throws=> error\n    (await-result (a/go (a/go {:error error})) :response) =throws=> error))\n\n(deftest wait-result-deref-test\n  (facts \"response\"\n    (await-result {:response :ctx} :response) => :ctx\n    (await-result (future {:response :ctx}) :response) => :ctx\n    (await-result (future (future {:response :ctx})) :response) => :ctx)\n  (facts \"exception\"\n    (await-result {:error error} :response) =throws=> error\n    (await-result (future {:error error}) :response) =throws=> error\n    (await-result (future (future {:error error})) :response) =throws=> error))\n\n(def deliver-result #'s/deliver-result)\n\n(defn fail! [_] (throw (ex-info \"should never get here\" {})))\n\n(let [p (promise)]\n  (deliver-result {:response :r} :response p fail!)\n  (fact\n    @p =eventually=> :r))\n\n(deftest deliver-result-test\n  (let [p (promise)]\n    (deliver-result {:response :r} :response p fail!)\n    (fact\n      @p =eventually=> :r))(let [p (promise)]\n                                 (deliver-result {:response :r} :response p fail!)\n                                 (fact @p =eventually=> :r))\n\n  (let [p (promise)]\n    (deliver-result {:response :r} :response p fail!)\n    (fact @p =eventually=> :r))\n\n  (let [p (promise)]\n    (deliver-result {:error (ex-info \"oh no\" {})} :response fail! p)\n    (fact @p =eventually=> (ex-info? \"oh no\" {})))\n\n  (let [p (promise)]\n    (deliver-result (a/go {:response :r}) :response p fail!)\n    (fact @p =eventually=> :r))\n\n  (let [p (promise)]\n    (deliver-result (a/go {:error (ex-info \"oh no\" {})}) :response fail! p)\n    (fact @p =eventually=> (ex-info? \"oh no\" {})))\n\n  (let [p (promise)]\n    (deliver-result (future (a/go {:response :r})) :response p fail!)\n    (fact @p =eventually=> :r)))\n"
  },
  {
    "path": "test/clj/sieppari/manifold_test.clj",
    "content": "(ns sieppari.manifold-test\n  (:require [clojure.test :refer :all]\n            [testit.core :refer :all]\n            [sieppari.core :as sc]\n            [manifold.deferred :as d]))\n\n(defn make-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) ctx)\n   :leave (fn [ctx] (swap! log conj [:leave name]) ctx)\n   :error (fn [ctx] (swap! log conj [:error name]) ctx)})\n\n(defn make-async-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) (d/success-deferred ctx))\n   :leave (fn [ctx] (swap! log conj [:leave name]) (d/success-deferred ctx))\n   :error (fn [ctx] (swap! log conj [:error name]) (d/success-deferred ctx))})\n\n(defn make-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    request))\n\n(defn make-async-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    (d/success-deferred request)))\n\n(def request {:foo \"bar\"})\n(def error (ex-info \"oh no\" {}))\n\n(defn fail! [& _]\n  (throw (ex-info \"Should never be called\" {})))\n\n(deftest setup-sync-test-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest setup-async-test-test\n  (let [log (atom [])\n        response (-> [(make-async-logging-interceptor log :a)\n                      (make-async-logging-interceptor log :b)\n                      (make-async-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-b-sync-execute-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-async-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-handler-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-stack-sync-execute-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      response => request)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:leave :c]\n                          [:leave :b]\n                          [:leave :a]])))\n\n(deftest async-stack-async-execute-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (make-async-logging-handler log)]\n        (sc/execute request (partial deliver response-p) fail!))\n    (fact\n      @response-p =eventually=> request)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:leave :c]\n                          [:leave :b]\n                          [:leave :a]])))\n\n(deftest async-execute-with-error-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (fn [_]\n           (swap! log conj [:handler])\n           (throw error))]\n        (sc/execute request fail! (partial deliver response-p)))\n    (fact\n      @response-p =eventually=> error)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:error :c]\n                          [:error :b]\n                          [:error :a]])))\n\n(deftest async-execute-rejection-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (fn [_]\n           (swap! log conj [:handler])\n           (d/error-deferred error))]\n        (sc/execute request fail! (partial deliver response-p)))\n    (fact\n      @response-p =eventually=> error)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:error :c]\n                          [:error :b]\n                          [:error :a]])))\n\n(deftest async-failing-handler-test\n  (let [log (atom [])]\n    (fact\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (d/success-deferred error))]\n          (sc/execute request))\n      =throws=> error)\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:error :c]\n               [:error :b]\n               [:error :a]])))\n\n(deftest async-failing-handler-b-fixes-test\n  (let [log (atom [])]\n    (fact\n      (-> [(make-logging-interceptor log :a)\n           (assoc (make-async-logging-interceptor log :b)\n             :error (fn [ctx]\n                      (swap! log conj [:error :b])\n                      (d/success-deferred\n                        (-> ctx\n                            (assoc :error nil)\n                            (assoc :response :fixed-by-b)))))\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (d/success-deferred error))]\n          (sc/execute request))\n      => :fixed-by-b)\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:error :c]\n               [:error :b]\n               [:leave :a]])))\n"
  },
  {
    "path": "test/clj/sieppari/promesa_test.clj",
    "content": "(ns sieppari.promesa-test\n  (:require [clojure.test :refer :all]\n            [testit.core :refer :all]\n            [sieppari.core :as sc]\n            [promesa.core :as p]))\n\n(defn make-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) ctx)\n   :leave (fn [ctx] (swap! log conj [:leave name]) ctx)\n   :error (fn [ctx] (swap! log conj [:error name]) ctx)})\n\n(defn make-async-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) (p/promise ctx))\n   :leave (fn [ctx] (swap! log conj [:leave name]) (p/promise ctx))\n   :error (fn [ctx] (swap! log conj [:error name]) (p/promise ctx))})\n\n(defn make-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    request))\n\n(defn make-async-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    (p/promise request)))\n\n(def request {:foo \"bar\"})\n(def error (ex-info \"oh no\" {}))\n\n(defn fail! [& _]\n  (throw (ex-info \"Should never be called\" {})))\n\n(deftest setup-sync-test-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest setup-async-test-test\n  (let [log (atom [])\n        response (-> [(make-async-logging-interceptor log :a)\n                      (make-async-logging-interceptor log :b)\n                      (make-async-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-b-sync-execute-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-async-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-handler-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:leave :c]\n               [:leave :b]\n               [:leave :a]])\n    (fact\n      response => request)))\n\n(deftest async-stack-sync-execute-test\n  (let [log (atom [])\n        response (-> [(make-logging-interceptor log :a)\n                      (make-logging-interceptor log :b)\n                      (make-logging-interceptor log :c)\n                      (make-async-logging-handler log)]\n                     (sc/execute request))]\n    (fact\n      response => request)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:leave :c]\n                          [:leave :b]\n                          [:leave :a]])))\n\n(deftest async-stack-async-execute-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (make-async-logging-handler log)]\n        (sc/execute request (partial deliver response-p) fail!))\n    (fact\n      @response-p =eventually=> request)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:leave :c]\n                          [:leave :b]\n                          [:leave :a]])))\n\n(deftest async-execute-with-error-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (fn [_]\n           (swap! log conj [:handler])\n           (throw error))]\n        (sc/execute request fail! (partial deliver response-p)))\n    (fact\n      @response-p =eventually=> error)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:error :c]\n                          [:error :b]\n                          [:error :a]])))\n\n(deftest async-execute-rejection-test\n  (let [log (atom [])\n        response-p (promise)]\n    (-> [(make-logging-interceptor log :a)\n         (make-logging-interceptor log :b)\n         (make-logging-interceptor log :c)\n         (fn [_]\n           (swap! log conj [:handler])\n           (p/rejected error))]\n        (sc/execute request fail! (partial deliver response-p)))\n    (fact\n      @response-p =eventually=> error)\n    (fact\n      @log =eventually=> [[:enter :a]\n                          [:enter :b]\n                          [:enter :c]\n                          [:handler]\n                          [:error :c]\n                          [:error :b]\n                          [:error :a]])))\n\n(deftest async-failing-handler-test\n  (let [log (atom [])]\n    (fact\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (p/resolved error))]\n          (sc/execute request))\n      =throws=> error)\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:error :c]\n               [:error :b]\n               [:error :a]])))\n\n(deftest async-failing-handler-b-fixes-test\n  (let [log (atom [])]\n    (fact\n      (-> [(make-logging-interceptor log :a)\n           (assoc (make-async-logging-interceptor log :b)\n             :error (fn [ctx]\n                      (swap! log conj [:error :b])\n                      (p/promise\n                        (-> ctx\n                            (assoc :error nil)\n                            (assoc :response :fixed-by-b)))))\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (p/resolved error))]\n          (sc/execute request))\n      => :fixed-by-b)\n    (fact\n      @log => [[:enter :a]\n               [:enter :b]\n               [:enter :c]\n               [:handler]\n               [:error :c]\n               [:error :b]\n               [:leave :a]])))\n"
  },
  {
    "path": "test/cljc/sieppari/async/core_async_test.cljc",
    "content": "(ns sieppari.async.core-async-test\n  (:require [clojure.test :refer [deftest is #?(:cljs async)]]\n            [sieppari.async :as as]\n            [sieppari.async.core-async]\n            [clojure.core.async :as a]))\n\n(deftest async?-test\n  (is (as/async? (a/go \"foo\"))))\n\n#?(:clj\n   (deftest core-async-continue-clj-promise-test\n     (let [respond (promise)]\n       (as/continue (a/go \"foo\") (partial deliver respond))\n       (is (= \"foo\" @respond))))\n   :cljs\n   (deftest core-async-continue-cljs-callback-test\n     (async done\n       (is (as/continue (a/go \"foo\")\n                        (fn [response]\n                          (is (= \"foo\" response))\n                          (done)))))))\n\n#?(:clj\n   (deftest core-async-catch-clj-promise-test\n     (let [respond (promise)]\n       (as/catch (a/go (Exception. \"fubar\")) (fn [_] (deliver respond \"foo\")))\n       (is (= \"foo\" @respond))))\n   :cljs\n   (deftest core-async-catch-cljs-callback-test\n     (async done\n       (is (as/continue (as/catch (a/go (js/Error. \"fubar\"))\n                                  (fn [_] \"foo\"))\n                        (fn [response]\n                          (is (= \"foo\" response))\n                          (done)))))))\n\n#?(:clj\n   (deftest await-test\n     (is (= \"foo\" (as/await (a/go \"foo\"))))))\n"
  },
  {
    "path": "test/cljc/sieppari/async/promesa_test.cljc",
    "content": "(ns sieppari.async.promesa-test\n  (:require [clojure.test :refer [deftest is #?(:cljs async)]]\n            [sieppari.async :as as]\n            [promesa.core :as p]\n            [promesa.exec :as px]))\n\n(deftest async?-test\n  (is (as/async? (p/promise 1))))\n\n#?(:clj\n   (deftest core-async-continue-cljs-callback-test\n     (let [respond (promise)\n           p (p/create\n               (fn [resolve _]\n                 (px/schedule! 10 #(resolve \"foo\"))))]\n       (as/continue p respond)\n       (is (= @respond \"foo\"))))\n   :cljs\n   (deftest core-async-continue-cljs-callback-test\n     (let [p (p/create\n               (fn [resolve _]\n                 (px/schedule! 10 #(resolve \"foo\"))))]\n       (async done\n         (is (as/continue p (fn [response]\n                              (is (= \"foo\" response))\n                              (done))))))))\n\n#?(:clj\n   (deftest core-async-catch-cljs-callback-test\n     (let [respond (promise)\n           p (p/create\n               (fn [_ reject]\n                 (px/schedule! 10 #(reject (Exception. \"fubar\")))))]\n       (as/catch p (fn [_] (respond \"foo\")))\n       (is (= @respond \"foo\"))))\n   :cljs\n   (deftest core-async-catch-cljs-callback-test\n     (let [p (p/create\n               (fn [_ reject]\n                 (px/schedule! 10 #(reject (js/Error. \"fubar\")))))]\n       (async done\n         (is (as/continue (as/catch p (fn [_] \"foo\"))\n                          (fn [response]\n                            (is (= \"foo\" response))\n                            (done))))))))\n\n#?(:clj\n   (deftest await-test\n     (is (= \"foo\"\n            (as/await (p/create\n                        (fn [resolve _]\n                          (px/schedule! 10 #(resolve \"foo\")))))))))\n"
  },
  {
    "path": "test/cljc/sieppari/context_test.cljc",
    "content": "(ns sieppari.context-test\n  (:require [clojure.test :refer [deftest is testing]]\n            [sieppari.context :as sc]\n            [sieppari.interceptor :as si]\n            [sieppari.queue :as sq]))\n\n(deftest terminate-test\n  (let [queue (sq/into-queue [{:name :a} {:name :b}])]\n    (is (= {:queue sq/empty-queue}\n           (sc/terminate {:queue queue})))\n\n    (is (= (sc/terminate {:queue queue} :the-response)\n           {:queue sq/empty-queue\n            :response :the-response}))))\n\n(deftest inject-test\n  (let [queue (sq/into-queue [{:name :a} {:name :b}])]\n    (is (= {:queue (conj sq/empty-queue\n                         (si/map->Interceptor {:name :x :enter nil :leave nil :error nil})\n                         (si/map->Interceptor {:name :a :enter nil :leave nil :error nil})\n                         (si/map->Interceptor {:name :b :enter nil :leave nil :error nil}))}\n           (sc/inject {:queue queue} {:name :x}))\n        \"it should add the x interceptor at the head of the queue\")\n\n    (is (instance? #?(:clj  clojure.lang.PersistentQueue\n                      :cljs cljs.core/PersistentQueue)\n                   (:queue (sc/inject {:queue queue} {:name :x})))\n        \"it should return a queue of interceptors\")))\n"
  },
  {
    "path": "test/cljc/sieppari/interceptor_test.cljc",
    "content": "(ns sieppari.interceptor-test\n  (:require [clojure.test :refer [deftest is testing]]\n            [sieppari.interceptor :as si])\n  #?(:clj (:import (sieppari.interceptor Interceptor))))\n\n(defn make-interceptor [value]\n  {:enter (fn [ctx]\n            (update ctx :foo + value))})\n\n(defn make-handler [value]\n  (fn [request]\n    (+ request value)))\n\n(deftest into-interceptor-test\n  (is (instance? #?(:clj Interceptor :cljs si/Interceptor) (si/into-interceptor {})) \"result is  record\")\n\n  (is (fn? (:enter (si/into-interceptor str))) \"functions can be made to interceptors\")\n\n  (is (= {:request 41 :response 42}\n         (-> inc si/into-interceptor :enter (apply [{:request 41}]))) \"functions are treated as request handlers\")\n\n  (is (= {:foo 42}\n         (-> [make-interceptor 10]\n             si/into-interceptor\n             :enter\n             (apply [{:foo 32}]))) \"vectors are evaluated, interceptor factory case\")\n\n  (is (= {:request 32 :response 42}\n         (-> [make-handler 10]\n             si/into-interceptor\n             :enter\n             (apply [{:request 32}]))) \"vectors are evaluated, handler factory case\")\n\n  (let [i (si/into-interceptor {:name \"test\"})]\n    (is (identical? i (si/into-interceptor i)) \"interceptors are already interceptors\"))\n\n  (is (nil? (si/into-interceptor nil)) \"nil punning\"))\n"
  },
  {
    "path": "test/cljc/sieppari/queue_test.cljc",
    "content": "(ns sieppari.queue-test\n  (:require [clojure.test :refer [deftest is testing]]\n            [sieppari.queue :as sq]\n            [sieppari.interceptor :as si])\n  #?(:clj (:import (clojure.lang PersistentQueue)\n                   (sieppari.interceptor Interceptor))))\n\n(deftest into-queue-test\n  (is (instance? #?(:clj PersistentQueue :cljs cljs.core.PersistentQueue)\n                 (sq/into-queue [{}])))\n\n  (is (instance? #?(:clj PersistentQueue :cljs cljs.core.PersistentQueue)\n                 (sq/into-queue '({}))))\n\n  (is (instance? #?(:clj PersistentQueue :cljs cljs.core.PersistentQueue)\n                 (sq/into-queue (cons {} nil))))\n\n  (let [the-queue (sq/into-queue [{}])]\n    (is (identical? the-queue (sq/into-queue the-queue))))\n\n  (is (every? (partial instance? #?(:clj Interceptor :cljs si/Interceptor))\n              (sq/into-queue [{}])))\n\n  (is (nil? (sq/into-queue [])))\n\n  (is (nil? (sq/into-queue '())))\n\n  (is (nil? (sq/into-queue nil))))\n"
  },
  {
    "path": "test/cljs/sieppari/core_async_test.cljs",
    "content": "(ns sieppari.core-async-test\n  (:require [clojure.test :refer [deftest is async]]\n            [clojure.core.async :refer [go]]\n            [sieppari.core :as sc]\n            [sieppari.async.core-async]))\n\n(defn make-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) ctx)\n   :leave (fn [ctx] (swap! log conj [:leave name]) ctx)\n   :error (fn [ctx] (swap! log conj [:error name]) ctx)})\n\n\n(defn make-async-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) (go ctx))\n   :leave (fn [ctx] (swap! log conj [:leave name]) (go ctx))\n   :error (fn [ctx] (swap! log conj [:error name]) (go ctx))})\n\n(defn make-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    request))\n\n(defn make-async-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    (go request)))\n\n(def request {:foo \"bar\"})\n(def error (ex-info \"oh no\" {}))\n\n(defn fail! [& _]\n  (throw (ex-info \"Should never be called\" {})))\n\n(deftest chan-happy-sync-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (make-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest chan-happy-async-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-async-logging-interceptor log :a)\n           (make-async-logging-interceptor log :b)\n           (make-async-logging-interceptor log :c)\n           (make-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest chan-async-b-sync-execute-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-async-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (make-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest chan-async-handler-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (make-async-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest chan-async-execute-with-handler-throws-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (throw error))]\n          (sc/execute request\n                      fail!\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:error :c]\n                                [:error :b]\n                                [:error :a]]))\n                        (is (= response error))\n                        (done)))))))\n\n(deftest chan-async-execute-rejection-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (go error))]\n          (sc/execute request\n                      fail!\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:error :c]\n                                [:error :b]\n                                [:error :a]]))\n                        (is (= response error))\n                        (done)))))))\n\n(deftest chan-async-failing-handler-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (go error))]\n          (sc/execute request\n                      fail!\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:error :c]\n                                [:error :b]\n                                [:error :a]]))\n                        (is (= response error))\n                        (done)))))))\n\n(deftest chan-async-failing-handler-b-fixes-test\n  (let [make-fixing-error-b (fn [log]\n                              (-> (make-async-logging-interceptor log :b)\n                                  (assoc :error (fn [ctx]\n                                                  (swap! log conj [:error :b])\n                                                  (go\n                                                    (-> ctx\n                                                        (assoc :error nil)\n                                                        (assoc :response :fixed-by-b)))))))]\n    (async done\n      (let [log (atom [])]\n        (-> [(make-logging-interceptor log :a)\n             (make-fixing-error-b log)\n             (make-logging-interceptor log :c)\n             (fn [_]\n               (swap! log conj [:handler])\n               (go error))]\n\n            (sc/execute request\n                        (fn [response]\n                          (is (= @log\n                                 [[:enter :a]\n                                  [:enter :b]\n                                  [:enter :c]\n                                  [:handler]\n                                  [:error :c]\n                                  [:error :b]\n                                  [:leave :a]]))\n                          (is (= response :fixed-by-b))\n                          (done))\n                        fail!))))))\n"
  },
  {
    "path": "test/cljs/sieppari/core_execute_test.cljs",
    "content": "(ns sieppari.core-execute-test\n  (:require [clojure.test :refer-macros [deftest is testing async]]\n            [sieppari.core :as sc]\n            [sieppari.context :as sctx]))\n\n;; Following tests use a test-chain that has some interceptors\n;; that fail on each stage function (enter, leave, error). The\n;; idea is that the tests override the expected stage functions\n;; with test specific function. This ensures that no unexpected\n;; stage functions are called.\n;;\n\n;; Make an interceptor with given name and set all stage functions\n;; to report unexpected invocation. Tests should override expected\n;; stages.\n\n(defn unexpected [name stage]\n  (fn [ctx]\n    (throw (ex-info \"unexpected invocation\"\n                    {:name name\n                     :stage stage\n                     :ctx ctx}))))\n\n(defn make-test-interceptor [name]\n  {:name name\n   :enter (unexpected name :enter)\n   :leave (unexpected name :leave)\n   :error (unexpected name :error)})\n\n; Test stack with three interceptors and a handler:\n\n(def test-chain [(make-test-interceptor :a)\n                 (make-test-interceptor :b)\n                 (make-test-interceptor :c)\n                 (unexpected :handler nil)])\n\n(def a-index 0)\n(def b-index 1)\n(def c-index 2)\n(def h-index 3)\n\n;; Helper: always throws an exception with specific marker\n;; in data part:\n\n(defn fail! [& _]\n  (throw (ex-info \"Should never be called\" {})))\n\n(def error (ex-info \"oh no\" {::error-marker true}))\n\n(defn always-throw [ctx]\n  (throw error))\n\n;; Helper: return error handler function that ensures\n;; that `ctx` contains an exception caused by `always-throw`,\n;; clears the exception and sets response to given response:\n\n(defn error-handler-interceptor [response]\n  (fn [ctx]\n    (assert (not (some? (:response ctx))))\n    (assert (-> ctx :error ex-data (= {::error-marker true})))\n    (-> ctx\n        (dissoc :error)\n        (assoc :response response))))\n\n;;\n;; Tests:\n;;\n\n(deftest happy-case-test\n  (async done\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] identity)\n        (assoc-in [h-index] inc)\n        (assoc-in [c-index :leave] identity)\n        (assoc-in [b-index :leave] identity)\n        (assoc-in [a-index :leave] identity)\n        (sc/execute 41\n                    (fn [response]\n                      (is (= 42 response) \"enable all enter and leave stages, use `inc` as handler\")\n                      (done))\n                    fail!))))\n\n(deftest enter-b-causes-exception-test\n  (async done\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] always-throw)\n        (assoc-in [b-index :error] identity)\n        (assoc-in [a-index :error] identity)\n        (sc/execute 41\n                    fail!\n                    (fn [err]\n                      (is (= error err) \":b causes an exception\")\n                      (done))))))\n\n(deftest enter-c-causes-exception-a-handles-test\n  (async done\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] identity)\n        (assoc-in [a-index :error] (error-handler-interceptor :fixed-by-a))\n        (sc/execute 41\n                    (fn [response]\n                      (is (= :fixed-by-a response) \":c enter causes an exception, :b sees error, :a handles\")\n                      (done))\n                    fail!))))\n\n(deftest enter-c-causes-exception-b-handles-test\n  (async done\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] (error-handler-interceptor :fixed-by-b))\n        (assoc-in [a-index :leave] identity)\n        (sc/execute 41\n                    (fn [response]\n                      (is (= :fixed-by-b response) \":c enter causes an exception, :b handles\")\n                      (done))\n                    fail!))))\n\n(deftest handler-causes-exception-b-handles-test\n  (async done\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] identity)\n        (assoc-in [c-index :enter] identity)\n        (assoc-in [h-index] always-throw)\n        (assoc-in [c-index :error] identity)\n        (assoc-in [b-index :error] (error-handler-interceptor :fixed-by-b))\n        (assoc-in [a-index :leave] identity)\n        (sc/execute 41\n                    (fn [response]\n                      (is (= :fixed-by-b response) \":c enter causes an exception, :b handles\")\n                      (done))\n                    fail!))))\n\n(deftest enter-b-sets-response-test\n  (async done\n    (-> test-chain\n        (assoc-in [a-index :enter] identity)\n        (assoc-in [b-index :enter] (fn [ctx] (sctx/terminate ctx :response-by-b)))\n        (assoc-in [b-index :leave] identity)\n        (assoc-in [a-index :leave] identity)\n        (sc/execute 41\n                    (fn [response]\n                      (is (= :response-by-b response) \":b sets the response, no invocation of :c nor :handler\")\n                      (done))\n                    fail!))))\n\n(deftest nil-response-test\n  (async done\n    (sc/execute [(constantly nil)] {}\n                (fn [response]\n                  (is (= nil response) \"nil response is allowed\")\n                  (done))\n                fail!)))\n\n(deftest nil-interceptors-test\n  (async done\n    (sc/execute nil {}\n                (fn [response]\n                  (is (= nil response) \"interceptor chain can be nil\")\n                  (done))\n                fail!)))\n\n(deftest empty-interceptors-test\n  (async done\n    (sc/execute [] {}\n                (fn [response]\n                  (is (= nil response) \"interceptor chain can be empty\")\n                  (done))\n                fail!)))\n\n(defn make-logging-interceptor [name]\n  {:name name\n   :enter (fn [ctx]\n            (update ctx :request conj [:enter name]))\n   :leave (fn [ctx]\n            (update ctx :response conj [:leave name]))})\n\n(defn logging-handler [request]\n  (conj request [:handler]))\n\n(deftest inject-interceptor-test\n  (async done\n    (-> [(make-logging-interceptor :a)\n         {:enter (fn [ctx] (sctx/inject ctx (make-logging-interceptor :x)))}\n         (make-logging-interceptor :c)\n         logging-handler]\n        (sc/execute []\n                    (fn [response]\n                      (is (= [[:enter :a]\n                              [:enter :x]\n                              [:enter :c]\n                              [:handler]\n                              [:leave :c]\n                              [:leave :x]\n                              [:leave :a]]\n                             response) \":b injects interceptor :x to chain, ensure the order is correct\")\n                      (done))\n                    fail!))))\n\n;; TODO: figure out how enqueue should work? Should enqueue add interceptors just\n;; before the handler?\n\n#_\n(deftest enqueue-interceptor-test\n  (fact \":b enqueues interceptor :x to chain, ensure the order is correct\"\n    (-> [(make-logging-interceptor :a)\n         {:enter (fn [ctx] (sc/enqueue ctx (make-logging-interceptor :x)))}\n         (make-logging-interceptor :c)\n         logging-handler]\n        (sctx/into-interceptors)\n        (sc/execute []))\n    => [[:enter :a]\n        [:enter :c]\n        [:enter :x]\n        [:handler]\n        [:leave :x]\n        [:leave :c]\n        [:leave :a]]))\n"
  },
  {
    "path": "test/cljs/sieppari/native_promise_test.cljs",
    "content": "(ns sieppari.native-promise-test\n  (:require [clojure.test :as test :refer-macros [deftest is testing async]]\n            [sieppari.core :as sc]\n            [sieppari.async]))\n\n(defn make-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (swap! log conj [:enter name]) ctx)\n   :leave (fn [ctx] (swap! log conj [:leave name]) ctx)\n   :error (fn [ctx] (swap! log conj [:error name]) ctx)})\n\n(defn make-async-logging-interceptor [log name]\n  {:name name\n   :enter (fn [ctx] (js/Promise. #(do (swap! log conj [:enter name]) (% ctx))))\n   :leave (fn [ctx] (js/Promise. #(do (swap! log conj [:leave name]) (% ctx))))\n   :error (fn [ctx] (js/Promise. #(do (swap! log conj [:error name]) (% ctx))))})\n\n(defn make-logging-handler [log]\n  (fn [request]\n    (swap! log conj [:handler])\n    request))\n\n(defn make-async-logging-handler [log]\n  (fn [request]\n    (js/Promise. #(do (swap! log conj [:handler]) (% request)))))\n\n(def request {:foo \"bar\"})\n(def error (ex-info \"oh no\" {}))\n\n(defn fail! [& _]\n  (throw (ex-info \"Should never be called\" {})))\n\n(deftest native-promise-happy-sync-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (make-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest native-promise-happy-async-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-async-logging-interceptor log :a)\n           (make-async-logging-interceptor log :b)\n           (make-async-logging-interceptor log :c)\n           (make-async-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest native-promise-async-b-sync-execute-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-async-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (make-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest native-promise-async-handler-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (make-async-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest native-promise-async-stack-async-execute-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (make-async-logging-handler log)]\n          (sc/execute request\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:leave :c]\n                                [:leave :b]\n                                [:leave :a]]))\n                        (is (= response request))\n                        (done))\n                      fail!)))))\n\n(deftest native-promise-async-execute-handler-throws-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (throw error))]\n          (sc/execute request\n                      fail!\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:error :c]\n                                [:error :b]\n                                [:error :a]]))\n                        (is (= response error))\n                        (done)))))))\n\n(deftest native-promise-async-failing-handler-test\n  (async done\n    (let [log (atom [])]\n      (-> [(make-logging-interceptor log :a)\n           (make-logging-interceptor log :b)\n           (make-logging-interceptor log :c)\n           (fn [_]\n             (swap! log conj [:handler])\n             (js/Promise.resolve error))]\n          (sc/execute request\n                      fail!\n                      (fn [response]\n                        (is (= @log\n                               [[:enter :a]\n                                [:enter :b]\n                                [:enter :c]\n                                [:handler]\n                                [:error :c]\n                                [:error :b]\n                                [:error :a]]))\n                        (is (= response error))\n                        (done)))))))\n\n(deftest native-promise-async-rejection-test\n         (async done\n                (let [log (atom [])]\n                     (-> [(make-logging-interceptor log :a)\n                          (make-logging-interceptor log :b)\n                          (make-logging-interceptor log :c)\n                          (fn [_]\n                              (swap! log conj [:handler])\n                              (js/Promise.reject error))]\n                         (sc/execute request\n                                     fail!\n                                     (fn [response]\n                                         (is (= @log\n                                                [[:enter :a]\n                                                 [:enter :b]\n                                                 [:enter :c]\n                                                 [:handler]\n                                                 [:error :c]\n                                                 [:error :b]\n                                                 [:error :a]]))\n                                         (is (= response error))\n                                         (done)))))))\n\n(deftest native-promise-async-failing-handler-b-fixes-test\n  (let [make-fixing-error-b (fn [log]\n                              (-> (make-async-logging-interceptor log :b)\n                                  (assoc :error (fn [ctx]\n                                                  (swap! log conj [:error :b])\n                                                  (js/Promise.resolve\n                                                   (-> ctx\n                                                       (assoc :error nil)\n                                                       (assoc :response :fixed-by-b)))))))]\n    (async done\n      (let [log (atom [])]\n        (-> [(make-logging-interceptor log :a)\n             (make-fixing-error-b log)\n             (make-logging-interceptor log :c)\n             (fn [_]\n               (swap! log conj [:handler])\n               (js/Promise.resolve error))]\n            (sc/execute request\n                        (fn [response]\n                          (is (= @log\n                                 [[:enter :a]\n                                  [:enter :b]\n                                  [:enter :c]\n                                  [:handler]\n                                  [:error :c]\n                                  [:error :b]\n                                  [:leave :a]]))\n                          (is (= response :fixed-by-b))\n                          (done))\n                        fail!))))))\n"
  },
  {
    "path": "tests.edn",
    "content": "#kaocha/v1\n{:tests [{:id         :unit\n          :test-paths [\"test/cljc\" \"test/clj\"]}\n         {:id         :unit-cljs\n          :type       :kaocha.type/cljs\n          :test-paths [\"test/cljc\" \"test/cljs\"]}]}\n"
  }
]