mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 06:20:32 +00:00
📚 Document promesa API shapes in memory
Two API families with inconsistent argument order, verified against the 12.0.1 jar: fmap/mcat are fn-first (they pair with ->>), then/catch are promise-first (they pair with ->). The bind-style ops require the fn to return a promise; a plain value crashes with "expected thenable". The repo convention prefers ->> composition, and legacy -> chains stay as they are. Routes from critical-info and exporter/core. AI-assisted-by: glm-5.3-flash
This commit is contained in:
parent
a9a105509b
commit
fe636282cb
73
.serena/memories/clojure/promesa.md
Normal file
73
.serena/memories/clojure/promesa.md
Normal file
@ -0,0 +1,73 @@
|
||||
# Promesa (funcool, 12.0.1) — verified API shapes
|
||||
|
||||
The promise lib of the monorepo: JVM (backend, `promesa.exec` thread
|
||||
pools) and CLJS (frontend, exporter). Declared in `common/deps.edn`;
|
||||
heavy users: `frontend/src/app/main/**` (Rx interop), `exporter/src/app/**`
|
||||
(management client, auth), `backend/src/app/storage/s3.clj` and
|
||||
`worker/cron.clj` (JVM await paths). Doc: `doc/promises.md` upstream.
|
||||
|
||||
## Two API families (verified against the 12.0.1 jar)
|
||||
|
||||
Argument order is inconsistent BY DESIGN; each op belongs to one of
|
||||
two families:
|
||||
|
||||
**promise-first `([p f ...])` — pairs with `->` threading** (the
|
||||
promise arrives as the threaded first argument):
|
||||
- `then`, `then'`, `bind`, `handle`, `finally`, `catch` (`([p f])`,
|
||||
2-arity with predicate: `([p pred-or-type f])`), `chain`
|
||||
`([p f & fs])`.
|
||||
|
||||
**fn-first `([f p ...])` — pairs with `->>` threading** (the promise
|
||||
arrives as the threaded LAST argument):
|
||||
- `map`/`fmap`, `mapcat`/`mcat`, `hmap`/`hcat`, `fnly`, `merr`.
|
||||
|
||||
Every 3-arity in a family inserts its executor FIRST for the fn-first
|
||||
family (`([executor f p])`) and LAST for the promise-first family
|
||||
(`([p f executor])`).
|
||||
|
||||
**What `f` must return:**
|
||||
- `then`/`catch`/`fmap`/`hmap`: plain value OK — auto unwrapping.
|
||||
- the bind-style ops `mcat` and `merr`: **`f` MUST return a promise
|
||||
instance** (`merr` may also throw). A plain value does NOT get
|
||||
wrapped: the impl's `fbind` pushes a BIND task that demands a
|
||||
thenable and crashes with `TypeError: expected thenable`
|
||||
(first-hand crash in the exporter worker, 2026-10-07).
|
||||
|
||||
`finally`/`fnly`: run `f`, mirror the original promise value.
|
||||
|
||||
## promesa's own -> / ->> macros (do NOT confuse with clojure.core)
|
||||
|
||||
`p/->` and `p/->>` exist in `promesa.core` (12.x). Their purpose is NOT
|
||||
arg positioning: they expand to a `chain` over `then` and REALIZE the
|
||||
promise at each step, so every form receives the realized value
|
||||
(e.g. `(p/-> (js/fetch ...) .-body .-status)`).
|
||||
|
||||
## Repo convention
|
||||
|
||||
**Prefer `->>` composition** over the map family (fn-first ops); it is
|
||||
how new promise code should read:
|
||||
|
||||
(->> (fetch ...)
|
||||
(p/fmap (fn [v] ...))
|
||||
(p/mcat (fn [v] (p/...))))
|
||||
|
||||
A `p/do` terminator inside `->>` receives the threaded promise as its
|
||||
LAST expression — it runs AFTER the earlier exprs, so it can never act
|
||||
as the chain's continuation; terminate by wrapping the chain in an
|
||||
explicit `(p/do chain ...)` (`p/let` avoids the same trap).
|
||||
|
||||
Legacy code threads promises with `->` over the promise-first
|
||||
family (`(-> p (p/then ...))`, `(p/then p f)` calls). Read the args in
|
||||
that order when touching it; do not rewrite it unless the task touches
|
||||
that code anyway.
|
||||
|
||||
For JVM blocking code, `promesa.exec/await!` exists but is deprecated
|
||||
since 12.0.0 → `p/await`.
|
||||
|
||||
## Async test gotcha (CLJS)
|
||||
|
||||
An async cljs.test var only proves its chain when the chain is really
|
||||
awaited: a broken link outside the `p/let`/`p/do` body ends silently as
|
||||
"Ran N tests containing 0 assertions" with 0 failures. If a test passes
|
||||
with 0 assertions, the chain broke upstream; add a print in the last
|
||||
link (`CHAIN RAN`) to prove plumbing before trusting green.
|
||||
@ -8,6 +8,7 @@ You are working on the GitHub project `penpot/penpot`, a monorepo.
|
||||
- Cross-cutting testing principles, TDD workflow, and anti-patterns: `mem:testing`.
|
||||
- Verified Clojure language behaviors that contradict common assumptions (e.g. `int?` covers `Long`; `integer?` is the general predicate): `mem:clojure/idioms` — read before assuming stdlib predicate semantics.
|
||||
- When designing a solution or an API, read `mem:clojure/design-rules` (function shape, boundary checks, naming).
|
||||
- Promise code (JVM + CLJS): promesa's two API families and their arg orders (fmap/mcat fn-first, then/catch promise-first, `mcat` f must return a promise), the repo `->>` preference, and async-test silent-green traps: `mem:clojure/promesa` — read before writing or reading promise chains.
|
||||
|
||||
# Development workflow
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
- Because exporter consumes `common/`, shared file/shape/model changes may need exporter verification even when the immediate change is not under `exporter/`.
|
||||
- Cross-cutting testing principles and anti-patterns: `mem:testing`.
|
||||
- Exporter test conventions and CI: `mem:exporter/testing`.
|
||||
- Promise chains in the exporter (management client, auth) follow promesa's two API families; arg orders, what each fn must return, the `->>` preference and the async-test silent-green trap: `mem:clojure/promesa` — read before touching promise code.
|
||||
|
||||
## HTTP and browser pool
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user