@suss/resolution 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,6 +28,8 @@ readsProperty(x, o, n) x is the expression o.n
28
28
  binds(x, y) the name x is declared as y
29
29
  endsHolding(x, y) the name x is written more than once and
30
30
  holds y once the writes have run
31
+ fallbackBranch(x, b) x is a fallback expression and b is one
32
+ of its branches
31
33
  paramOf(f, k, p) p is f's parameter at position k
32
34
  paramNamed(f, n, p) p is f's parameter called n
33
35
  extends(c, b) class c is written as extending b
@@ -79,6 +81,18 @@ A language with a hop of its own states it as a step too. JavaScript's
79
81
  `.bind` and Ruby's `Const.new` are each one rule, and every question
80
82
  below picks them up without being told.
81
83
 
84
+ A fallback expression (`a || b`, `a ?? b`, Python's `a or b`) says its
85
+ value is one of its branches, so each branch is a value step. That is
86
+ the whole rule. When one branch is something no static reader can
87
+ settle, a global cache or a parameter, that branch derives nothing, and
88
+ the branch that does resolve is the only claim the source makes. The
89
+ idiomatic client singleton, `global.prisma || new PrismaClient()`, is
90
+ exactly this: the global read makes no claim, so the construction is
91
+ the answer. When both branches resolve to different things, both
92
+ derive, and the caller's single-answer policy refuses the pair. That is
93
+ the same refusal every other two-candidate chain gets, because a value
94
+ that is one of two different things is not one thing.
95
+
82
96
  Each question is that one closure with its own stopping condition. So
83
97
  adding a construct is one step and every question gets it, and adding a
84
98
  question is a stopping condition and no steps at all.
@@ -159,6 +173,30 @@ than a copy of every other. It differs from `callsFunction`, which
159
173
  starts from the function because a caller asking for call sites has the
160
174
  function in hand.
161
175
 
176
+ ## Explaining an answer
177
+
178
+ Evaluate the same rules under `@suss/datalog`'s `witnesses` algebra and
179
+ every derived fact keeps the rule that fired and the facts it consumed,
180
+ so `proofOf` can rebuild the derivation tree of any answer on demand.
181
+ `explainResolutionProof` flattens that tree into the chain a person
182
+ reads: the step rules are the hops, `reaches` is the glue between them,
183
+ and the base cases ("x is already a function") end a chain without
184
+ adding to it. Each hop comes back with one sentence saying why it is
185
+ true, written from the rule's own name: `alias`, `import`, `argument`,
186
+ `factory unwrap`, and so on.
187
+
188
+ Two kinds of detail ride under a hop rather than beside it. A barrel
189
+ chain under an `import` hop lists which files forwarded the name. A
190
+ `declared wrapper` hop rests on a pack's word rather than on source, so
191
+ it surfaces as an assumption: "a pack declares that withSentry from
192
+ @sentry/serverless passes argument 0 through to its result". A proof
193
+ cut short by the depth cap says so instead of trailing off.
194
+
195
+ Atoms in a proof are whatever node ids the adapter interned, so both
196
+ functions take a `describe` callback that says an atom in source terms.
197
+ `renderExplanation` turns the flattened chain into printable lines;
198
+ `suss ask 'why does … reach …'` is this pipeline end to end.
199
+
162
200
  ## Why rules and not a walker
163
201
 
164
202
  Each rule describes one hop. The chains people write are longer than