<commit_before>= Plugins

Pathom allows a parser to have a collection of plugins that modify its behavior. Plugins is a top-level option when creating the parser, and the value is a vector of plugins:

[source,clojure]
----
(def parser (p/parser {::p/plugins [...]}))
----

== Built-in plugins

Pathom provides some common usage plugins:

* xref:connect/basics.adoc[connect-plugin]
* xref:core/error-handling.adoc[error-handler-plugin]
* elide-special-outputs-plugin
* env-plugin
* env-wrap-plugin
* pre-process-parser-plugin
* post-process-parser-plugin
* raise-mutation-result-plugin

== Plugin Skeleton

In this section we'll be using a few plugins to make our lives easier.

Plugins set code that wraps some of pathom operations, a plugin is a map where you bind
keys from event names to functions. They work on `wrap` fashion, kind like `ring` wrappers.
Here is what a plugin looks like:

[source,clojure]
----
(ns pathom-docs.plugin-example
  (:require [com.wsscode.pathom.core :as p]))

(def my-plugin
  ; the ::p/wrap-parser entry point wraps the entire parser,
  ; this means it wraps the operation that runs once on each
  ; query that runs with the parser
  {::p/wrap-parser
   (fn [parser]
     ; here you can initialize stuff that runs only once per
     ; parser, like a durable cache across requests
     (fn [env tx]
       ; here you could initialize per-request items, things
       ; that needs to be set up once per query as we do on
       ; request cache, or the error atom to accumulate errors

       ; in this case, we are doing nothing, just calling the
       ; previous parser, a pass-through wrapper if you may
       (parser env tx)))

   ; this wraps the read function, meaning it will run once for
   ; each recursive parser call that happens during your query

   ::p/wrap-read
   (fn [reader]
     (fn [env]
       ; here you can wrap the parse read, in pathom we use this
       ; on the error handler to do the try/catch per node, also
       ; the profiler use this point to calculate the time spent
       ; on a given node

       ; this is also a good point to inject custom read keys if
       ; you need to, the profile plugin, for example, can capture
       ; the key ::p.profile/profile and export the current profile
       ; information
       (reader env)))

   ;; during the connect processing, while the wrap-read will work around
   ;; the entire attribute, this wraps each individual resolver call (excluding cache hits)
   ::pc/wrap-resolve
   (fn [resolve]
     (fn [env input]
       (resolve env input)))

   ::p/wrap-mutate
      ; mutation wrappers require a slightly different pattern
      ; as the actual mutation comes on an ':action' key
      (fn [mutate]
       (fn [env k params]
         ; inject custom mutation keys, etc here
         (let [out (mutate env k params)]
           (cond-> out
             {:action out}
             (update :action
               (fn [action]
                 (fn []<commit_msg>plugin doc: {:action env} should be (:action env)<commit_after>= Plugins

Pathom allows a parser to have a collection of plugins that modify its behavior. Plugins is a top-level option when creating the parser, and the value is a vector of plugins:

[source,clojure]
----
(def parser (p/parser {::p/plugins [...]}))
----

== Built-in plugins

Pathom provides some common usage plugins:

* xref:connect/basics.adoc[connect-plugin]
* xref:core/error-handling.adoc[error-handler-plugin]
* elide-special-outputs-plugin
* env-plugin
* env-wrap-plugin
* pre-process-parser-plugin
* post-process-parser-plugin
* raise-mutation-result-plugin

== Plugin Skeleton

In this section we'll be using a few plugins to make our lives easier.

Plugins set code that wraps some of pathom operations, a plugin is a map where you bind
keys from event names to functions. They work on `wrap` fashion, kind like `ring` wrappers.
Here is what a plugin looks like:

[source,clojure]
----
(ns pathom-docs.plugin-example
  (:require [com.wsscode.pathom.core :as p]))

(def my-plugin
  ; the ::p/wrap-parser entry point wraps the entire parser,
  ; this means it wraps the operation that runs once on each
  ; query that runs with the parser
  {::p/wrap-parser
   (fn [parser]
     ; here you can initialize stuff that runs only once per
     ; parser, like a durable cache across requests
     (fn [env tx]
       ; here you could initialize per-request items, things
       ; that needs to be set up once per query as we do on
       ; request cache, or the error atom to accumulate errors

       ; in this case, we are doing nothing, just calling the
       ; previous parser, a pass-through wrapper if you may
       (parser env tx)))

   ; this wraps the read function, meaning it will run once for
   ; each recursive parser call that happens during your query

   ::p/wrap-read
   (fn [reader]
     (fn [env]
       ; here you can wrap the parse read, in pathom we use this
       ; on the error handler to do the try/catch per node, also
       ; the profiler use this point to calculate the time spent
       ; on a given node

       ; this is also a good point to inject custom read keys if
       ; you need to, the profile plugin, for example, can capture
       ; the key ::p.profile/profile and export the current profile
       ; information
       (reader env)))

   ;; during the connect processing, while the wrap-read will work around
   ;; the entire attribute, this wraps each individual resolver call (excluding cache hits)
   ::pc/wrap-resolve
   (fn [resolve]
     (fn [env input]
       (resolve env input)))

   ::p/wrap-mutate
      ; mutation wrappers require a slightly different pattern
      ; as the actual mutation comes on an ':action' key
      (fn [mutate]
       (fn [env k params]
         ; inject custom mutation keys, etc here
         (let [out (mutate env k params)]
           (cond-> out
             (:action out)
             (update :action
               (fn [action]
                 (fn []
{'fraction_non_alphanumeric': 0.08224891558224892, 'fraction_numerical': 0.0, 'mean_word_length': 2.728233830845771, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2220804', 'n_tokens_mistral': 1683, 'n_tokens_neox': 1626, 'n_words': 779}