com.biffweb

add-seconds

(add-seconds date seconds)
Returns a new java.util.Date with the given number of seconds added.

anom

(anom category & [message & [opts]])
Constructs an anomaly.

Example: (anom :incorrect
               "Invalid parameter"
               {:info "x should be an integer"})

See https://github.com/cognitect-labs/anomalies

anomaly?

(anomaly? x)
Returns true if x is an anomaly.

See https://github.com/cognitect-labs/anomalies

assoc-db

(assoc-db {:keys [biff.xtdb/node], :as sys})
Sets :biff/db on the system map to (xt/db node)

base-html

(base-html {:base/keys [title description lang image icon url canonical font-families head], :as opts} & contents)
Wraps contents in an :html and :body element with various metadata set.

font-families:  A collection of families to request from Google fonts (see g-fonts).
head:           Additional Rum elements to include inside the head.

base64-decode

(base64-decode string)
Converts a base64 string to a byte array.

base64-encode

(base64-encode bytes)
Converts a byte array to a base64 string.

between-hours?

(between-hours? t h1 h2)
Returns true if t is between the hours of h1 and h2 UTC.

For example:
(between-hours? #inst "2022-03-27T14:18:34.360-00:00" 14 16)
=> true

catchall

macro

(catchall & body)
Wraps body in (try ... (catch Exception _ nil))

crop-date

(crop-date date format)
Passes date through format-date and parse-date, in order to remove any
information not captured in the format.

For example:
(crop-date #inst "2022-03-27T09:13:34.182-00:00" "yyyy")
=> #inst "2022-01-01T08:00:00.000-00:00"

crop-day

(crop-day date)
Same as (crop-date "yyyy-MM-dd")

elapsed?

(elapsed? t1 t2 x unit)
Returns true if t2 occurs at least x units after t1.

unit can be :seconds, :minutes, :hours, :days, or :weeks.

emdash

A Rum data structure for an em dash.

endash

A Rum data structure for an en dash.

eval-files!

(eval-files! {:keys [biff/eval-paths], :or {eval-paths ["src"]}})
Evaluates any modified files and their dependents via clojure.tools.namespace.

export-rum

(export-rum pages dir)
Generate HTML files and write them to a directory.

pages:  A map from paths to Rum data structures, e.g.
        {"/" [:div "hello"]}. Paths that end in / will have index.html
        appended to them.
dir:    A path to the root directory where the files should be saved, e.g.
        "target/resources/public".

fix-print

macro

(fix-print & body)
Ensures that print output doesn't get swallowed by e.g. an editor nrepl plugin.

Binds *out*, *err* and *flush-on-newline* to their root values.

(fix-print
  (println "hello"))

form

(form {:keys [hidden], :as opts} & body)
Returns a [:form ...] element.

hidden:  A map from names to values, which will be converted to
         [:input {:type "hidden" ...}] fields.
opts:    Options for the :form element (with hidden removed).

Sets :method to "post" by default, and includes a CSRF token (via
ring.middleware.anti-forgery/*anti-forgery-token*).

format-date

(format-date date & [format])
Formats date using java.text.SimpleDateFormat.

If format isn't provided, uses rfc3339.

g-fonts

(g-fonts families)
Returns a link element for requesting families from Google fonts.

For example:
(g-fonts ["Nunito+Sans:wght@900"])
=> [:link {:rel "stylesheet", :href ...}]

generate-secret

(generate-secret length)
Generates a random byte array and returns it as a base64 string.

The bytes are generated with buddy.core.nonce/random-bytes, which uses a
secure random number generator.

jwt-decrypt

(jwt-decrypt token secret)
Convenience wrapper for buddy.sign.jwt/decrypt.

token is a string as returned by jwt-encrypt. secret is a base64-encoded
string that was used to encrypt token. Returns the claims passed to
jwt-encrypt. Returns nil if the token is invalid or expired.

jwt-encrypt

(jwt-encrypt {:keys [exp-in], :as claims} secret)
Convenience wrapper for buddy.sign.jwt/encrypt.

Returns a string token. secret is a base64-encoded string used to encrypt the
token. A secret can be generated with (com.biffweb/generate-secret 32).
exp-in is the number of seconds in the future at which the token should
expire. claims is passed to buddy.sign.jwt/encrypt as-is, except that :exp is
set based on exp-in.

lazy-q

(lazy-q db query & args)
Calls xtdb.api/open-q and passes a lazy seq of the results to a function.

Accepts the same arguments as xtdb.api/open-q, except the last argument is a
function which must process the results eagerly. Also includes the same
functionality as biff/q.

letd

macro

(letd bindings & body)
Like let, but transparently wraps all bindings with delay.

Examples:

(macroexpand-1 '(letd [a 1]
                  a))
=> (let [a (delay 1)]
     @a)

(letd [a (do (println "a evaluated")
             1)
       {:keys [b]} (do (println "b evaluated")
                       {:b 2})
       [_ _ c] (do (println "c evaluated")
                   [1 2 3])]
  (if (even? b)
    a
    c))
=>
(out) b evaluated
(out) a evaluated
1

lookup

(lookup db k v)
Returns the first document found with the given key and value.

For example:
(lookup db :user/email "hello@example.com")
=> {:xt/id #uuid "...", :user/email "hello@example.com"}

lookup-id

(lookup-id db k v)
Returns the ID of the first document found with the given key and value.

For example:
(lookup db :user/email "hello@example.com")
=> #uuid "..."

mailersend

(mailersend {:keys [mailersend/api-key mailersend/defaults], :as sys} opts)
Sends an email with MailerSend.

See https://developers.mailersend.com/api/v1/email.html#send-an-email. Does a
POST request on the /v1/email endpoint and returns the X-Message-Id response
header on success. On failure, prints an error message and returns false.

opts is a map which will be converted to JSON and included as the body of the
request. defaults is a map from paths to default values. It will be combined
with opts. For example:

(mailersend {:mailersend/api-key "..."
             :mailersend/defaults {[:from :email] "hello@mail.example.com"
                                   [:from :name] "My Application"
                                   [:reply_to :email] "hello@example.com"
                                   [:reply_to :name] "My Application"}}
            {:to [{:email "recipient@example.com"}]
             :subject "Some subject"
             :text "Some text"
             :from {:name "This will override the default value of 'My Application'"}})

nbsp

A Rum data structure for a non-breaking space.

normalize-email

(normalize-email email)
Normalizes an email address to make future lookups easier.

Trims leading and trailing whitespace and converts to lower case. Returns nil
if the email is empty after trimming.

now

(now)
Same as (java.util.Date.)

parse-date

(parse-date date & [format])
Parses date (a string) using java.text.SimpleDateFormat.

If format isn't provided, uses rfc3339.

pprint

(pprint & args)
Alias of clojure.pprint/pprint

q

(q db query & args)
Convenience wrapper for xtdb.api/q.

If the :find value is not a vector, results will be passed through
(map first ...). Also throws an exception if (count args) doesn't match
(count (:in query)).

refresh

(refresh)
Stops the system, refreshes source files, and restarts the system.

The system is stopped by calling all the functions in (:biff/stop
@com.biffweb/system). (:biff/after-refresh @system) is a fully-qualified
symbol which will be resolved and called after refreshing. See
https://biffweb.com/docs/#system-composition

reitit-handler

(reitit-handler {:keys [router routes on-error], :as opts})
Convenience wrapper for reitit.ring/ring-handler.

Only one of router or routes needs to be given. If you pass in routes, it
will be wrapped with (reitit.ring/router routes). on-error is an optional
Ring handler. The request map passed to it will include a :status key (either
404, 405, or 406).

Includes reitit.ring/redirect-trailing-slash-handler.

render

(render body)
Renders body with rum/render-static-markup and returns a 200 response.

rfc3339

Same as "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", for use with parse-date and format-date.

safe-merge

(safe-merge & ms)
Like merge, but throws an exception if any maps share keys.

select-ns-as

(select-ns-as m ns-from ns-to)
Selects and renames keys from m based on the namespace.

Examples:

(select-ns-as {:foo/a 1, :foo.bar/b 2, :baz/c 3} 'foo 'quux)
=> {:quux/a 1, :quux.bar/b 2}

(select-ns-as {:foo/a 1, :foo.bar/b 2, :baz/c 3} 'foo nil)
=> {:a 1, :bar/b 2}

sh

(sh & args)
Runs a shell command.

Returns the output if successful; otherwise, throws an exception.

sha256

(sha256 string)
Returns the SHA256 hash of string.

start-node

(start-node {:keys [topology dir opts jdbc-spec pool-opts kv-store], :or {kv-store :rocksdb}, :as options})
A higher-level version of xtdb.api/start-node.

Calls xtdb.api/sync before returning the node.

topology:   One of #{:standalone :jdbc}.
kv-store:   One of #{:rocksdb :lmdb}. Default :rocksdb
dir:        A path to store RocksDB instances in.
jdbc-spec,
pool-opts:  Used only when topology is :jdbc. Passed in as
            {:xtdb.jdbc/connection-pool
             {:db-spec jdbc-spec :pool-opts pool-opts ...}}.
opts:       Additional options to pass to xtdb.api/start-node.

start-system

(start-system init)
Starts a system from an initial system map.

Stores the system in the com.biffweb/system atom. See
https://biffweb.com/docs/#system-composition

submit-tx

(submit-tx {:keys [biff.xtdb/node], :as sys} biff-tx)
High-level wrapper over xtdb.api/submit-tx.

See https://biffweb.com/docs/#transactions.

system

unsafe

(unsafe html)
Returns {:dangerouslySetInnerHTML {:__html html}}, for use with Rum.

use-chime

(use-chime {:biff.chime/keys [tasks], :as sys})
A Biff component for running scheduled tasks with Chime.

See https://github.com/jarohen/chime. tasks is a collection of maps, for
example:

[{:task (fn [system] (println "hello there"))
  :schedule (iterate #(biff/add-seconds % 60) (java.util.Date.))}]

This value of tasks would print "hello there" every 60 seconds. task is a
single-argument function that receives the system map. schedule is a
zero-argument function that returns a (possibly infinite) sequence of times
at which to run the task function.

use-config

(use-config sys)
Reads config from (:biff/config sys), and edn file, and merges into sys.

The config file's contents should be a map from environments to config keys
and values, for example:

{:prod {:host "example.com"
        :port 8080}
 :dev {:merge [:prod]
       :host "localhost"}}

The current environment should be stored in the BIFF_ENV environment variable.
The default value is `prod`. To inherit config from other environments, set
:merge to a sequence of environment keys.

use-hawk

(use-hawk {:biff.hawk/keys [on-save exts paths], :or {paths ["src" "resources"]}, :as sys})
A Biff component that runs code when files are changed, via Hawk.

See (https://github.com/wkf/hawk).

on-save:  A single-argument function to call whenever a file is saved.
          Receives the system map as a parameter. The function is called no
          more than once every 500 milliseconds.
paths:    A collection of root directories to monitor for file changes.
exts:     If exts is non-empty, files that don't end in one of the extensions
          will be ignored.

use-jetty

(use-jetty {:biff/keys [host port handler], :or {host "localhost", port 8080}, :as sys})
A Biff component that starts a Jetty web server.

use-outer-default-middleware

(use-outer-default-middleware sys)
A Biff component that wraps :biff/handler with middleware that depends on the system map.

Includes wrap-ring-defaults and wrap-env.

use-random-default-secrets

(use-random-default-secrets sys)
A Biff component that merges temporary secrets into the system map if needed.

Sets :biff.middleware/cookie-secret and :biff/jwt-secret if they are nil. The
secrets will not persist if the system is restarted. Can be useful in
development. e.g. a config.edn.TEMPLATE can be checked into a project's
repository without secrets set. Contributers can run the project by copying
the file to config.edn, without needing to modify it.

This component should not be relied upon in production; instead you should
save secrets in config.edn. This is done automatically at project setup time
for new Biff projects.

use-tx-listener

(use-tx-listener {:keys [biff.xtdb/on-tx biff.xtdb/node], :as sys})
If on-tx is provided, starts an XTDB transaction listener.

Calls on-tx whenever a new transaction is successfully indexed. on-tx
receives the system map and the transaction, i.e. (on-tx system tx). tx is
the transaction as returned by (xtdb.api/open-tx-log node tx-id true). on-tx
will not be called concurrently: if a second transaction is indexed while
on-tx is still running, use-tx-listener will wait until it finishes.

use-when

(use-when f & components)
Passes the system map to components only if (f system) is true.

See https://biffweb.com/docs/#system-composition

use-xt

(use-xt {:biff.xtdb/keys [topology kv-store dir opts], :as sys})
A Biff component that starts an XTDB node.

Sets :biff.xtdb/node on the system map. topology, kv-store, dir and opts are
passed to start-node. Any keys matching :biff.xtdb.jdbc/* or
:biff.xtdb.jdbc-pool/* are passed in as jdbc-spec and pool-opts,
respectively.

wrap-anti-forgery-websockets

(wrap-anti-forgery-websockets handler)
Ensure that websocket upgrade requests pass a CSRF check.

If the client requests a websocket upgrade, the Origin header must be the
same as the :biff/base-url key in the request map. Otherwise a 403
response is given.

wrap-env

(wrap-env handler system)
Merges the system map with incoming requests and sets :biff/db.

See assoc-db.

wrap-index-files

(wrap-index-files handler {:keys [index-files], :or {index-files ["index.html"]}, :as opts})
If handler returns nil, try again with each index file appended to the URI in turn.

wrap-inner-defaults

(wrap-inner-defaults handler opts)
Wraps handler with various middleware which don't depend on the system map.

Includes wrap-log-requests, wrap-internal-error, wrap-resource, and
Muuntaja's wrap-params and wrap-format (see https://github.com/metosin/muuntaja).
opts is passed to wrap-resource and wrap-internal-error.

This function can wrap a Ring handler outside of a call to biff/start-system,
For example:

(def handler (wrap-inner-defaults ... {}))

(defn start []
  (biff/start-system
    {:biff/handler #'handler
     ...}))

This way, handler and its middleware can be redefined from the repl without
needing to restart the system.

wrap-internal-error

(wrap-internal-error handler #:biff.middleware{:keys [on-error]})
Catches exceptions from handler, prints a stack trace, and returns a 500 response.

You may optionally provide on-error, a single-argument function that receives
the request map with the :status key set to 500. The default implementation
returns a plain Internal Server Error message.

wrap-log-requests

(wrap-log-requests handler)
Prints execution time, status, request method, uri and query params for each request.

wrap-render-rum

(wrap-render-rum handler)
If handler returns a vector, pass it to rum.core/render-static-markup and return a 200 response.

wrap-resource

(wrap-resource handler {:biff.middleware/keys [root index-files], :or {root "public", index-files ["index.html"]}, :as opts})
Serves static resources with ring.middleware.resource/wrap-resource-request.

root:         The resource root from which static files should be served.
index-files:  See wrap-index-files.

Checks for a static resource first. If none is found, passes the request to
handler.

wrap-ring-defaults

(wrap-ring-defaults handler {:biff.middleware/keys [session-store cookie-secret secure session-max-age], :or {session-max-age (* 60 60 24 60), secure true}, :as opts})
Wraps handler with ring.middleware.defaults/wrap-defaults.

secure:          if true, uses ring.middleware.defaults/secure-site-defaults,
                 else uses site-defaults.
cookie-secret:   if provided, session-store is set with
                 ring.middleware.session.cookie/cookie-store
session-store:   passed to wrap-defaults under the [:session :store] path.
sesion-max-age:  the number of seconds after which a session should expire.

Disables CSRF checks. You must wrap non-API routes with
ring.middleware.anti-forgery. The Biff project template does this by default.
Disables SSL redirects under the assumption that this is handled by e.g.
NGINX. Also sets SameSite=Lax explicitly on the session cookie.