@suss/resolution 0.1.0 → 0.1.1
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 +83 -10
- package/dist/index.cjs +160 -21
- package/dist/index.d.cts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +158 -21
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -19,7 +19,14 @@ factory in TypeScript.
|
|
|
19
19
|
|
|
20
20
|
```
|
|
21
21
|
func(f) f is a function
|
|
22
|
+
objectValue(o) o is an object written out literally
|
|
23
|
+
writtenValue(x) x is an expression written out in source
|
|
24
|
+
rather than a name for one
|
|
25
|
+
holdsProperty(o, n, x) object o holds x under the name n
|
|
26
|
+
readsProperty(x, o, n) x is the expression o.n
|
|
22
27
|
binds(x, y) the name x is declared as y
|
|
28
|
+
endsHolding(x, y) the name x is written more than once and
|
|
29
|
+
holds y once the writes have run
|
|
23
30
|
paramOf(f, k, p) p is f's parameter at position k
|
|
24
31
|
returnsValue(f, v) f returns v
|
|
25
32
|
bodyCalls(f, c) f's body calls c
|
|
@@ -40,6 +47,52 @@ match.
|
|
|
40
47
|
|
|
41
48
|
Node identity is the adapter's business. The rules only join on it.
|
|
42
49
|
|
|
50
|
+
## What comes out
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
comesTo(x, z) following x arrives at the value z
|
|
54
|
+
resolves(x, z) comesTo narrowed to functions
|
|
55
|
+
isWrittenAs(x, z) x is written as the expression z
|
|
56
|
+
comesFrom(x, m, n) following x arrives at m's export n
|
|
57
|
+
callsInto(f, m, n) calling f ends up calling m's n
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`resolves` is the question most callers ask. `comesTo` is the one
|
|
61
|
+
underneath it, and it can answer with an object, since a chain has to
|
|
62
|
+
pass through objects for `routes.list` to reach what `list` holds.
|
|
63
|
+
|
|
64
|
+
`isWrittenAs` follows the same names to the expression a value is
|
|
65
|
+
written as, whatever kind of expression that turns out to be. A GraphQL
|
|
66
|
+
document is neither a function nor an object, so `comesTo` never
|
|
67
|
+
reaches one.
|
|
68
|
+
|
|
69
|
+
`comesFrom` answers the direction `comesTo` cannot. Every `comesTo`
|
|
70
|
+
chain ends at something written out in the source being read, so a name
|
|
71
|
+
for a library's own function ends nowhere: the library's body is not
|
|
72
|
+
here. `comesFrom` follows the same aliases, imports and barrels and
|
|
73
|
+
answers with the module and the name that module exports.
|
|
74
|
+
|
|
75
|
+
`callsInto` puts that together with the calls a function makes. A
|
|
76
|
+
project writes its own decorator that calls `Resolver()` and applies
|
|
77
|
+
that one to its classes, and the class is a resolver even though nothing
|
|
78
|
+
about it says `Resolver`. Several answers for one function is the normal
|
|
79
|
+
case rather than an ambiguity, since a wrapper composing two library
|
|
80
|
+
decorators applies both, so a caller asks whether the one it cares about
|
|
81
|
+
is among them.
|
|
82
|
+
|
|
83
|
+
One relation comes out for the rules' own use rather than for callers:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
objectOf(x, obj) x stands for the object literal obj
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
An object arrives two ways, through a name or as what a factory call
|
|
90
|
+
returns, and `objectOf` names that step so the rule for `routes.list`
|
|
91
|
+
and the rule for `make(body).handle` are the same rule. A factory call
|
|
92
|
+
gets an `objectOf` answer without getting a `comesTo` answer, since a
|
|
93
|
+
factory call usually is the wrapper and answering with the function it
|
|
94
|
+
returns would fight the unwrapping answer.
|
|
95
|
+
|
|
43
96
|
## Why rules and not a walker
|
|
44
97
|
|
|
45
98
|
Each rule describes one hop. The chains people write are longer than
|
|
@@ -49,19 +102,20 @@ barrel re-exporting a wrapper. The engine composes what it has, so those
|
|
|
49
102
|
work without being named.
|
|
50
103
|
|
|
51
104
|
The corollary is where to look when something comes back empty. Suspect
|
|
52
|
-
the facts before the rules.
|
|
53
|
-
handlers and
|
|
54
|
-
|
|
105
|
+
the facts before the rules. Against one production service suss
|
|
106
|
+
resolved 11 handlers and missed most of what the template declared. The
|
|
107
|
+
fix was one condition in fact extraction, and the existing rules found
|
|
108
|
+
the rest unchanged.
|
|
55
109
|
|
|
56
110
|
## What is not modelled
|
|
57
111
|
|
|
58
112
|
**A handler passed as one property of a config.** `make({ body:
|
|
59
113
|
handler })`, where the factory reads `opts.body`, does not resolve. A
|
|
60
|
-
rule for it was tried and taken out:
|
|
61
|
-
callbacks off the same config
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
114
|
+
rule for it was tried and taken out: a wrapper often reads several
|
|
115
|
+
callbacks off the same config object, the rule made each one a
|
|
116
|
+
candidate for the whole call, and the resulting ambiguity nulled out
|
|
117
|
+
handlers that used to resolve. Saying which property is the handler
|
|
118
|
+
takes something structure does not carry,
|
|
65
119
|
either "the only property called" (which needs negation) or a pack
|
|
66
120
|
naming the property. Until one of those exists, this shape stays
|
|
67
121
|
unresolved on purpose.
|
|
@@ -69,8 +123,27 @@ unresolved on purpose.
|
|
|
69
123
|
**An element of an array.** `all[0]` has no fact for what an array
|
|
70
124
|
holds.
|
|
71
125
|
|
|
72
|
-
**
|
|
73
|
-
|
|
126
|
+
**Which write a read sees, once control flow decides it.** A name
|
|
127
|
+
written twice in a module's own statement list is answered: those
|
|
128
|
+
statements run once each, top to bottom, so the last write is what
|
|
129
|
+
anything importing the name gets, and the adapter says so with
|
|
130
|
+
`endsHolding`. A write inside a branch, a loop, or a function body is a
|
|
131
|
+
different claim, and the adapter stays quiet. The name then comes to
|
|
132
|
+
nothing, which is the answer until there are control-flow facts to
|
|
133
|
+
reason over.
|
|
134
|
+
|
|
135
|
+
Answering it in general is reaching definitions, per use rather than
|
|
136
|
+
per name. That needs facts saying which statement follows which and
|
|
137
|
+
which branch each sits on, and no adapter emits any today.
|
|
138
|
+
|
|
139
|
+
The adapter picking the write rather than a rule picking it is a cost
|
|
140
|
+
decision. Ordering writes inside the rules means asking which writes
|
|
141
|
+
have no later write, negation says that in one line, and this
|
|
142
|
+
evaluator has to throw its last fixpoint away and start over whenever a
|
|
143
|
+
rule set uses negation. The store evaluates after every wave of facts,
|
|
144
|
+
so one negated rule turned a 66 second run on the Saleor dashboard into
|
|
145
|
+
one that had not finished in ten minutes. Source order is something
|
|
146
|
+
every adapter already knows.
|
|
74
147
|
|
|
75
148
|
**Ambiguity is the caller's problem.** When the rules reach two
|
|
76
149
|
different functions the store returns nothing, since picking one would
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
ANSWER_RELATIONS: () => ANSWER_RELATIONS,
|
|
24
|
+
RESOLUTION_QUESTIONS: () => RESOLUTION_QUESTIONS,
|
|
23
25
|
RESOLUTION_RULES: () => RESOLUTION_RULES
|
|
24
26
|
});
|
|
25
27
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -35,6 +37,16 @@ var RESOLUTION_RULES = [
|
|
|
35
37
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
36
38
|
[(0, import_datalog.lit)("binds", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"))]
|
|
37
39
|
),
|
|
40
|
+
// A name written more than once holds the value the last write left
|
|
41
|
+
// there, and `binds` says nothing about it. The adapter works out
|
|
42
|
+
// which write that is, in its own language's terms, and stays quiet
|
|
43
|
+
// when control flow decides. A name it stays quiet about comes to no
|
|
44
|
+
// value, which is the answer.
|
|
45
|
+
(0, import_datalog.rule)(
|
|
46
|
+
"comesTo",
|
|
47
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
48
|
+
[(0, import_datalog.lit)("endsHolding", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"))]
|
|
49
|
+
),
|
|
38
50
|
// An import comes to what the module exports under that name.
|
|
39
51
|
(0, import_datalog.rule)(
|
|
40
52
|
"comesTo",
|
|
@@ -67,15 +79,37 @@ var RESOLUTION_RULES = [
|
|
|
67
79
|
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m2"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("value"))
|
|
68
80
|
]
|
|
69
81
|
),
|
|
70
|
-
//
|
|
71
|
-
// `comesTo`
|
|
72
|
-
//
|
|
82
|
+
// The object an expression stands for. A name arrives at one by
|
|
83
|
+
// following `comesTo` through aliases and imports. A factory call
|
|
84
|
+
// arrives at one through what the function it calls returns; the
|
|
85
|
+
// call itself is given no `comesTo`, since a factory call usually IS
|
|
86
|
+
// the wrapper and answering with the raw returned function would
|
|
87
|
+
// fight the unwraps answer.
|
|
88
|
+
(0, import_datalog.rule)(
|
|
89
|
+
"objectOf",
|
|
90
|
+
[(0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")],
|
|
91
|
+
[(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")), (0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("obj"))]
|
|
92
|
+
),
|
|
93
|
+
(0, import_datalog.rule)(
|
|
94
|
+
"objectOf",
|
|
95
|
+
[(0, import_datalog.variable)("r"), (0, import_datalog.variable)("obj")],
|
|
96
|
+
[
|
|
97
|
+
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
98
|
+
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
99
|
+
(0, import_datalog.lit)("returnsValue", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("ret")),
|
|
100
|
+
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("ret"), (0, import_datalog.variable)("obj")),
|
|
101
|
+
(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("obj"))
|
|
102
|
+
]
|
|
103
|
+
),
|
|
104
|
+
// Reading a property comes to what the object holds under that name,
|
|
105
|
+
// whichever way the object arrived: `routes.list` off a name, or
|
|
106
|
+
// `make(body).handle` off a call.
|
|
73
107
|
(0, import_datalog.rule)(
|
|
74
108
|
"comesTo",
|
|
75
109
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
76
110
|
[
|
|
77
111
|
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("o"), (0, import_datalog.variable)("n")),
|
|
78
|
-
(0, import_datalog.lit)("
|
|
112
|
+
(0, import_datalog.lit)("objectOf", (0, import_datalog.variable)("o"), (0, import_datalog.variable)("obj")),
|
|
79
113
|
(0, import_datalog.lit)("holdsProperty", (0, import_datalog.variable)("obj"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("held")),
|
|
80
114
|
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("held"), (0, import_datalog.variable)("z"))
|
|
81
115
|
]
|
|
@@ -153,23 +187,6 @@ var RESOLUTION_RULES = [
|
|
|
153
187
|
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("h"))
|
|
154
188
|
]
|
|
155
189
|
),
|
|
156
|
-
// A property read off a call: `make(body).handle`. The call itself is
|
|
157
|
-
// not given a `comesTo`, since a factory call usually IS the wrapper
|
|
158
|
-
// and answering with the raw returned function would fight the
|
|
159
|
-
// unwraps answer. Only the property read narrows enough to be safe.
|
|
160
|
-
(0, import_datalog.rule)(
|
|
161
|
-
"comesTo",
|
|
162
|
-
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
163
|
-
[
|
|
164
|
-
(0, import_datalog.lit)("readsProperty", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("r"), (0, import_datalog.variable)("n")),
|
|
165
|
-
(0, import_datalog.lit)("call", (0, import_datalog.variable)("r"), (0, import_datalog.variable)("c")),
|
|
166
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("f")),
|
|
167
|
-
(0, import_datalog.lit)("returnsValue", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("ret")),
|
|
168
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("ret"), (0, import_datalog.variable)("obj")),
|
|
169
|
-
(0, import_datalog.lit)("holdsProperty", (0, import_datalog.variable)("obj"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("held")),
|
|
170
|
-
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("held"), (0, import_datalog.variable)("z"))
|
|
171
|
-
]
|
|
172
|
-
),
|
|
173
190
|
// Wrapper transparency, declared: a pack says this callee wraps
|
|
174
191
|
// argument k, whatever its implementation looks like. The callee has
|
|
175
192
|
// to have been imported from the library the pack named, so a local
|
|
@@ -186,6 +203,57 @@ var RESOLUTION_RULES = [
|
|
|
186
203
|
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("a"), (0, import_datalog.variable)("h"))
|
|
187
204
|
]
|
|
188
205
|
),
|
|
206
|
+
// Where a name comes from, when what it names lives outside the
|
|
207
|
+
// source being read. `comesTo` ends at something written out, so it
|
|
208
|
+
// never reaches a library's own function, and a project's alias of a
|
|
209
|
+
// library name has no answer at all. This follows the same aliases
|
|
210
|
+
// and barrels in the other direction and answers with the module and
|
|
211
|
+
// the name that module exports.
|
|
212
|
+
(0, import_datalog.rule)(
|
|
213
|
+
"comesFrom",
|
|
214
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
215
|
+
[(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))]
|
|
216
|
+
),
|
|
217
|
+
(0, import_datalog.rule)(
|
|
218
|
+
"comesFrom",
|
|
219
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
220
|
+
[(0, import_datalog.lit)("binds", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("comesFrom", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))]
|
|
221
|
+
),
|
|
222
|
+
(0, import_datalog.rule)(
|
|
223
|
+
"comesFrom",
|
|
224
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
225
|
+
[
|
|
226
|
+
(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m2"), (0, import_datalog.variable)("n2")),
|
|
227
|
+
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m2"), (0, import_datalog.variable)("n2"), (0, import_datalog.variable)("value")),
|
|
228
|
+
(0, import_datalog.lit)("comesFrom", (0, import_datalog.variable)("value"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))
|
|
229
|
+
]
|
|
230
|
+
),
|
|
231
|
+
// Calling f ends up calling the name n that module m exports. One hop
|
|
232
|
+
// is f's own body, including the closures it declares; the other hop
|
|
233
|
+
// is a function f's body calls, so a wrapper of a wrapper answers
|
|
234
|
+
// with what the innermost one reaches.
|
|
235
|
+
//
|
|
236
|
+
// Several answers for one function is the normal case, not an
|
|
237
|
+
// ambiguity: a project decorator that composes two library decorators
|
|
238
|
+
// applies both. A caller asks whether a particular (m, n) is among
|
|
239
|
+
// them.
|
|
240
|
+
(0, import_datalog.rule)(
|
|
241
|
+
"callsInto",
|
|
242
|
+
[(0, import_datalog.variable)("f"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
243
|
+
[
|
|
244
|
+
(0, import_datalog.lit)("bodyCallsDeep", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("c")),
|
|
245
|
+
(0, import_datalog.lit)("comesFrom", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))
|
|
246
|
+
]
|
|
247
|
+
),
|
|
248
|
+
(0, import_datalog.rule)(
|
|
249
|
+
"callsInto",
|
|
250
|
+
[(0, import_datalog.variable)("f"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
251
|
+
[
|
|
252
|
+
(0, import_datalog.lit)("bodyCallsDeep", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("c")),
|
|
253
|
+
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("c"), (0, import_datalog.variable)("g")),
|
|
254
|
+
(0, import_datalog.lit)("callsInto", (0, import_datalog.variable)("g"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))
|
|
255
|
+
]
|
|
256
|
+
),
|
|
189
257
|
// The question callers ask: what a value comes to, narrowed to the
|
|
190
258
|
// functions. Objects appear in the middle of chains and never in an
|
|
191
259
|
// answer.
|
|
@@ -193,9 +261,80 @@ var RESOLUTION_RULES = [
|
|
|
193
261
|
"resolves",
|
|
194
262
|
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
195
263
|
[(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")), (0, import_datalog.lit)("func", (0, import_datalog.variable)("z"))]
|
|
264
|
+
),
|
|
265
|
+
// Following a name to the expression it is written as. A separate
|
|
266
|
+
// relation rather than a widening of `comesTo`, so a caller asking
|
|
267
|
+
// the older question never sees a new answer: a factory call already
|
|
268
|
+
// has an unwrapping answer, and letting it also answer with itself
|
|
269
|
+
// would make that pair ambiguous.
|
|
270
|
+
//
|
|
271
|
+
// The two seeds are the two ways an expression can be written out. An
|
|
272
|
+
// object literal is one of them, and it is the shape a generated
|
|
273
|
+
// GraphQL document takes.
|
|
274
|
+
(0, import_datalog.rule)("isWrittenAs", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("writtenValue", (0, import_datalog.variable)("x"))]),
|
|
275
|
+
(0, import_datalog.rule)("isWrittenAs", [(0, import_datalog.variable)("x"), (0, import_datalog.variable)("x")], [(0, import_datalog.lit)("objectValue", (0, import_datalog.variable)("x"))]),
|
|
276
|
+
(0, import_datalog.rule)(
|
|
277
|
+
"isWrittenAs",
|
|
278
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
279
|
+
[(0, import_datalog.lit)("binds", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"))]
|
|
280
|
+
),
|
|
281
|
+
(0, import_datalog.rule)(
|
|
282
|
+
"isWrittenAs",
|
|
283
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
284
|
+
[(0, import_datalog.lit)("endsHolding", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("y")), (0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("y"), (0, import_datalog.variable)("z"))]
|
|
285
|
+
),
|
|
286
|
+
(0, import_datalog.rule)(
|
|
287
|
+
"isWrittenAs",
|
|
288
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
289
|
+
[
|
|
290
|
+
(0, import_datalog.lit)("imports", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")),
|
|
291
|
+
(0, import_datalog.lit)("moduleExport", (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"), (0, import_datalog.variable)("value")),
|
|
292
|
+
(0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("value"), (0, import_datalog.variable)("z"))
|
|
293
|
+
]
|
|
196
294
|
)
|
|
197
295
|
];
|
|
296
|
+
var RESOLUTION_QUESTIONS = [
|
|
297
|
+
(0, import_datalog.rule)(
|
|
298
|
+
"wantedResolves",
|
|
299
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
300
|
+
[(0, import_datalog.lit)("wanted", (0, import_datalog.variable)("x")), (0, import_datalog.lit)("resolves", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"))]
|
|
301
|
+
),
|
|
302
|
+
(0, import_datalog.rule)(
|
|
303
|
+
"wantedComesTo",
|
|
304
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
305
|
+
[(0, import_datalog.lit)("wanted", (0, import_datalog.variable)("x")), (0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"))]
|
|
306
|
+
),
|
|
307
|
+
(0, import_datalog.rule)(
|
|
308
|
+
"wantedComesTo",
|
|
309
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
310
|
+
[(0, import_datalog.lit)("wantedOrigin", (0, import_datalog.variable)("x")), (0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"))]
|
|
311
|
+
),
|
|
312
|
+
(0, import_datalog.rule)(
|
|
313
|
+
"wantedIsWrittenAs",
|
|
314
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("z")],
|
|
315
|
+
[(0, import_datalog.lit)("wanted", (0, import_datalog.variable)("x")), (0, import_datalog.lit)("isWrittenAs", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("z"))]
|
|
316
|
+
),
|
|
317
|
+
(0, import_datalog.rule)(
|
|
318
|
+
"wantedComesFrom",
|
|
319
|
+
[(0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
320
|
+
[(0, import_datalog.lit)("wantedOrigin", (0, import_datalog.variable)("x")), (0, import_datalog.lit)("comesFrom", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))]
|
|
321
|
+
),
|
|
322
|
+
(0, import_datalog.rule)(
|
|
323
|
+
"wantedCallsInto",
|
|
324
|
+
[(0, import_datalog.variable)("g"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n")],
|
|
325
|
+
[
|
|
326
|
+
(0, import_datalog.lit)("wantedOrigin", (0, import_datalog.variable)("x")),
|
|
327
|
+
(0, import_datalog.lit)("comesTo", (0, import_datalog.variable)("x"), (0, import_datalog.variable)("g")),
|
|
328
|
+
(0, import_datalog.lit)("callsInto", (0, import_datalog.variable)("g"), (0, import_datalog.variable)("m"), (0, import_datalog.variable)("n"))
|
|
329
|
+
]
|
|
330
|
+
)
|
|
331
|
+
];
|
|
332
|
+
var ANSWER_RELATIONS = [
|
|
333
|
+
...new Set(RESOLUTION_QUESTIONS.map((r) => r.head.relation))
|
|
334
|
+
];
|
|
198
335
|
// Annotate the CommonJS export names for ESM import in node:
|
|
199
336
|
0 && (module.exports = {
|
|
337
|
+
ANSWER_RELATIONS,
|
|
338
|
+
RESOLUTION_QUESTIONS,
|
|
200
339
|
RESOLUTION_RULES
|
|
201
340
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -5,5 +5,25 @@ import * as _suss_datalog from '@suss/datalog';
|
|
|
5
5
|
* rules onto these before evaluating.
|
|
6
6
|
*/
|
|
7
7
|
declare const RESOLUTION_RULES: _suss_datalog.Rule[];
|
|
8
|
+
/**
|
|
9
|
+
* The questions a caller asks, written as rules. Two facts say somebody
|
|
10
|
+
* is asking: `wanted(x)` for what a value is, and `wantedOrigin(x)` for
|
|
11
|
+
* where a name came from. Each answer relation holds the pairs for the
|
|
12
|
+
* values somebody asked about, keyed by the value asked about, which is
|
|
13
|
+
* the key the caller looks up by anyway.
|
|
14
|
+
*
|
|
15
|
+
* Written down rather than left to each caller because the engine reads
|
|
16
|
+
* them. `deriveOnDemand` follows a chain only as far as one of these
|
|
17
|
+
* questions reaches it, and a caller that answered its own questions by
|
|
18
|
+
* scanning a relation would give the engine nothing to work from.
|
|
19
|
+
*
|
|
20
|
+
* The two asking facts are kept apart because they pull on different
|
|
21
|
+
* rules. Following a name back to its library goes through every call
|
|
22
|
+
* the value's function makes, so a caller that only wants to know what a
|
|
23
|
+
* handler resolves to should not pay for that.
|
|
24
|
+
*/
|
|
25
|
+
declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
|
|
26
|
+
/** The relations `RESOLUTION_QUESTIONS` answers into. */
|
|
27
|
+
declare const ANSWER_RELATIONS: string[];
|
|
8
28
|
|
|
9
|
-
export { RESOLUTION_RULES };
|
|
29
|
+
export { ANSWER_RELATIONS, RESOLUTION_QUESTIONS, RESOLUTION_RULES };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,5 +5,25 @@ import * as _suss_datalog from '@suss/datalog';
|
|
|
5
5
|
* rules onto these before evaluating.
|
|
6
6
|
*/
|
|
7
7
|
declare const RESOLUTION_RULES: _suss_datalog.Rule[];
|
|
8
|
+
/**
|
|
9
|
+
* The questions a caller asks, written as rules. Two facts say somebody
|
|
10
|
+
* is asking: `wanted(x)` for what a value is, and `wantedOrigin(x)` for
|
|
11
|
+
* where a name came from. Each answer relation holds the pairs for the
|
|
12
|
+
* values somebody asked about, keyed by the value asked about, which is
|
|
13
|
+
* the key the caller looks up by anyway.
|
|
14
|
+
*
|
|
15
|
+
* Written down rather than left to each caller because the engine reads
|
|
16
|
+
* them. `deriveOnDemand` follows a chain only as far as one of these
|
|
17
|
+
* questions reaches it, and a caller that answered its own questions by
|
|
18
|
+
* scanning a relation would give the engine nothing to work from.
|
|
19
|
+
*
|
|
20
|
+
* The two asking facts are kept apart because they pull on different
|
|
21
|
+
* rules. Following a name back to its library goes through every call
|
|
22
|
+
* the value's function makes, so a caller that only wants to know what a
|
|
23
|
+
* handler resolves to should not pay for that.
|
|
24
|
+
*/
|
|
25
|
+
declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
|
|
26
|
+
/** The relations `RESOLUTION_QUESTIONS` answers into. */
|
|
27
|
+
declare const ANSWER_RELATIONS: string[];
|
|
8
28
|
|
|
9
|
-
export { RESOLUTION_RULES };
|
|
29
|
+
export { ANSWER_RELATIONS, RESOLUTION_QUESTIONS, RESOLUTION_RULES };
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,16 @@ var RESOLUTION_RULES = [
|
|
|
11
11
|
[v("x"), v("z")],
|
|
12
12
|
[lit("binds", v("x"), v("y")), lit("comesTo", v("y"), v("z"))]
|
|
13
13
|
),
|
|
14
|
+
// A name written more than once holds the value the last write left
|
|
15
|
+
// there, and `binds` says nothing about it. The adapter works out
|
|
16
|
+
// which write that is, in its own language's terms, and stays quiet
|
|
17
|
+
// when control flow decides. A name it stays quiet about comes to no
|
|
18
|
+
// value, which is the answer.
|
|
19
|
+
rule(
|
|
20
|
+
"comesTo",
|
|
21
|
+
[v("x"), v("z")],
|
|
22
|
+
[lit("endsHolding", v("x"), v("y")), lit("comesTo", v("y"), v("z"))]
|
|
23
|
+
),
|
|
14
24
|
// An import comes to what the module exports under that name.
|
|
15
25
|
rule(
|
|
16
26
|
"comesTo",
|
|
@@ -43,15 +53,37 @@ var RESOLUTION_RULES = [
|
|
|
43
53
|
lit("moduleExport", v("m2"), v("n"), v("value"))
|
|
44
54
|
]
|
|
45
55
|
),
|
|
46
|
-
//
|
|
47
|
-
// `comesTo`
|
|
48
|
-
//
|
|
56
|
+
// The object an expression stands for. A name arrives at one by
|
|
57
|
+
// following `comesTo` through aliases and imports. A factory call
|
|
58
|
+
// arrives at one through what the function it calls returns; the
|
|
59
|
+
// call itself is given no `comesTo`, since a factory call usually IS
|
|
60
|
+
// the wrapper and answering with the raw returned function would
|
|
61
|
+
// fight the unwraps answer.
|
|
62
|
+
rule(
|
|
63
|
+
"objectOf",
|
|
64
|
+
[v("o"), v("obj")],
|
|
65
|
+
[lit("comesTo", v("o"), v("obj")), lit("objectValue", v("obj"))]
|
|
66
|
+
),
|
|
67
|
+
rule(
|
|
68
|
+
"objectOf",
|
|
69
|
+
[v("r"), v("obj")],
|
|
70
|
+
[
|
|
71
|
+
lit("call", v("r"), v("c")),
|
|
72
|
+
lit("comesTo", v("c"), v("f")),
|
|
73
|
+
lit("returnsValue", v("f"), v("ret")),
|
|
74
|
+
lit("comesTo", v("ret"), v("obj")),
|
|
75
|
+
lit("objectValue", v("obj"))
|
|
76
|
+
]
|
|
77
|
+
),
|
|
78
|
+
// Reading a property comes to what the object holds under that name,
|
|
79
|
+
// whichever way the object arrived: `routes.list` off a name, or
|
|
80
|
+
// `make(body).handle` off a call.
|
|
49
81
|
rule(
|
|
50
82
|
"comesTo",
|
|
51
83
|
[v("x"), v("z")],
|
|
52
84
|
[
|
|
53
85
|
lit("readsProperty", v("x"), v("o"), v("n")),
|
|
54
|
-
lit("
|
|
86
|
+
lit("objectOf", v("o"), v("obj")),
|
|
55
87
|
lit("holdsProperty", v("obj"), v("n"), v("held")),
|
|
56
88
|
lit("comesTo", v("held"), v("z"))
|
|
57
89
|
]
|
|
@@ -129,23 +161,6 @@ var RESOLUTION_RULES = [
|
|
|
129
161
|
lit("comesTo", v("a"), v("h"))
|
|
130
162
|
]
|
|
131
163
|
),
|
|
132
|
-
// A property read off a call: `make(body).handle`. The call itself is
|
|
133
|
-
// not given a `comesTo`, since a factory call usually IS the wrapper
|
|
134
|
-
// and answering with the raw returned function would fight the
|
|
135
|
-
// unwraps answer. Only the property read narrows enough to be safe.
|
|
136
|
-
rule(
|
|
137
|
-
"comesTo",
|
|
138
|
-
[v("x"), v("z")],
|
|
139
|
-
[
|
|
140
|
-
lit("readsProperty", v("x"), v("r"), v("n")),
|
|
141
|
-
lit("call", v("r"), v("c")),
|
|
142
|
-
lit("comesTo", v("c"), v("f")),
|
|
143
|
-
lit("returnsValue", v("f"), v("ret")),
|
|
144
|
-
lit("comesTo", v("ret"), v("obj")),
|
|
145
|
-
lit("holdsProperty", v("obj"), v("n"), v("held")),
|
|
146
|
-
lit("comesTo", v("held"), v("z"))
|
|
147
|
-
]
|
|
148
|
-
),
|
|
149
164
|
// Wrapper transparency, declared: a pack says this callee wraps
|
|
150
165
|
// argument k, whatever its implementation looks like. The callee has
|
|
151
166
|
// to have been imported from the library the pack named, so a local
|
|
@@ -162,6 +177,57 @@ var RESOLUTION_RULES = [
|
|
|
162
177
|
lit("comesTo", v("a"), v("h"))
|
|
163
178
|
]
|
|
164
179
|
),
|
|
180
|
+
// Where a name comes from, when what it names lives outside the
|
|
181
|
+
// source being read. `comesTo` ends at something written out, so it
|
|
182
|
+
// never reaches a library's own function, and a project's alias of a
|
|
183
|
+
// library name has no answer at all. This follows the same aliases
|
|
184
|
+
// and barrels in the other direction and answers with the module and
|
|
185
|
+
// the name that module exports.
|
|
186
|
+
rule(
|
|
187
|
+
"comesFrom",
|
|
188
|
+
[v("x"), v("m"), v("n")],
|
|
189
|
+
[lit("imports", v("x"), v("m"), v("n"))]
|
|
190
|
+
),
|
|
191
|
+
rule(
|
|
192
|
+
"comesFrom",
|
|
193
|
+
[v("x"), v("m"), v("n")],
|
|
194
|
+
[lit("binds", v("x"), v("y")), lit("comesFrom", v("y"), v("m"), v("n"))]
|
|
195
|
+
),
|
|
196
|
+
rule(
|
|
197
|
+
"comesFrom",
|
|
198
|
+
[v("x"), v("m"), v("n")],
|
|
199
|
+
[
|
|
200
|
+
lit("imports", v("x"), v("m2"), v("n2")),
|
|
201
|
+
lit("moduleExport", v("m2"), v("n2"), v("value")),
|
|
202
|
+
lit("comesFrom", v("value"), v("m"), v("n"))
|
|
203
|
+
]
|
|
204
|
+
),
|
|
205
|
+
// Calling f ends up calling the name n that module m exports. One hop
|
|
206
|
+
// is f's own body, including the closures it declares; the other hop
|
|
207
|
+
// is a function f's body calls, so a wrapper of a wrapper answers
|
|
208
|
+
// with what the innermost one reaches.
|
|
209
|
+
//
|
|
210
|
+
// Several answers for one function is the normal case, not an
|
|
211
|
+
// ambiguity: a project decorator that composes two library decorators
|
|
212
|
+
// applies both. A caller asks whether a particular (m, n) is among
|
|
213
|
+
// them.
|
|
214
|
+
rule(
|
|
215
|
+
"callsInto",
|
|
216
|
+
[v("f"), v("m"), v("n")],
|
|
217
|
+
[
|
|
218
|
+
lit("bodyCallsDeep", v("f"), v("c")),
|
|
219
|
+
lit("comesFrom", v("c"), v("m"), v("n"))
|
|
220
|
+
]
|
|
221
|
+
),
|
|
222
|
+
rule(
|
|
223
|
+
"callsInto",
|
|
224
|
+
[v("f"), v("m"), v("n")],
|
|
225
|
+
[
|
|
226
|
+
lit("bodyCallsDeep", v("f"), v("c")),
|
|
227
|
+
lit("comesTo", v("c"), v("g")),
|
|
228
|
+
lit("callsInto", v("g"), v("m"), v("n"))
|
|
229
|
+
]
|
|
230
|
+
),
|
|
165
231
|
// The question callers ask: what a value comes to, narrowed to the
|
|
166
232
|
// functions. Objects appear in the middle of chains and never in an
|
|
167
233
|
// answer.
|
|
@@ -169,8 +235,79 @@ var RESOLUTION_RULES = [
|
|
|
169
235
|
"resolves",
|
|
170
236
|
[v("x"), v("z")],
|
|
171
237
|
[lit("comesTo", v("x"), v("z")), lit("func", v("z"))]
|
|
238
|
+
),
|
|
239
|
+
// Following a name to the expression it is written as. A separate
|
|
240
|
+
// relation rather than a widening of `comesTo`, so a caller asking
|
|
241
|
+
// the older question never sees a new answer: a factory call already
|
|
242
|
+
// has an unwrapping answer, and letting it also answer with itself
|
|
243
|
+
// would make that pair ambiguous.
|
|
244
|
+
//
|
|
245
|
+
// The two seeds are the two ways an expression can be written out. An
|
|
246
|
+
// object literal is one of them, and it is the shape a generated
|
|
247
|
+
// GraphQL document takes.
|
|
248
|
+
rule("isWrittenAs", [v("x"), v("x")], [lit("writtenValue", v("x"))]),
|
|
249
|
+
rule("isWrittenAs", [v("x"), v("x")], [lit("objectValue", v("x"))]),
|
|
250
|
+
rule(
|
|
251
|
+
"isWrittenAs",
|
|
252
|
+
[v("x"), v("z")],
|
|
253
|
+
[lit("binds", v("x"), v("y")), lit("isWrittenAs", v("y"), v("z"))]
|
|
254
|
+
),
|
|
255
|
+
rule(
|
|
256
|
+
"isWrittenAs",
|
|
257
|
+
[v("x"), v("z")],
|
|
258
|
+
[lit("endsHolding", v("x"), v("y")), lit("isWrittenAs", v("y"), v("z"))]
|
|
259
|
+
),
|
|
260
|
+
rule(
|
|
261
|
+
"isWrittenAs",
|
|
262
|
+
[v("x"), v("z")],
|
|
263
|
+
[
|
|
264
|
+
lit("imports", v("x"), v("m"), v("n")),
|
|
265
|
+
lit("moduleExport", v("m"), v("n"), v("value")),
|
|
266
|
+
lit("isWrittenAs", v("value"), v("z"))
|
|
267
|
+
]
|
|
172
268
|
)
|
|
173
269
|
];
|
|
270
|
+
var RESOLUTION_QUESTIONS = [
|
|
271
|
+
rule(
|
|
272
|
+
"wantedResolves",
|
|
273
|
+
[v("x"), v("z")],
|
|
274
|
+
[lit("wanted", v("x")), lit("resolves", v("x"), v("z"))]
|
|
275
|
+
),
|
|
276
|
+
rule(
|
|
277
|
+
"wantedComesTo",
|
|
278
|
+
[v("x"), v("z")],
|
|
279
|
+
[lit("wanted", v("x")), lit("comesTo", v("x"), v("z"))]
|
|
280
|
+
),
|
|
281
|
+
rule(
|
|
282
|
+
"wantedComesTo",
|
|
283
|
+
[v("x"), v("z")],
|
|
284
|
+
[lit("wantedOrigin", v("x")), lit("comesTo", v("x"), v("z"))]
|
|
285
|
+
),
|
|
286
|
+
rule(
|
|
287
|
+
"wantedIsWrittenAs",
|
|
288
|
+
[v("x"), v("z")],
|
|
289
|
+
[lit("wanted", v("x")), lit("isWrittenAs", v("x"), v("z"))]
|
|
290
|
+
),
|
|
291
|
+
rule(
|
|
292
|
+
"wantedComesFrom",
|
|
293
|
+
[v("x"), v("m"), v("n")],
|
|
294
|
+
[lit("wantedOrigin", v("x")), lit("comesFrom", v("x"), v("m"), v("n"))]
|
|
295
|
+
),
|
|
296
|
+
rule(
|
|
297
|
+
"wantedCallsInto",
|
|
298
|
+
[v("g"), v("m"), v("n")],
|
|
299
|
+
[
|
|
300
|
+
lit("wantedOrigin", v("x")),
|
|
301
|
+
lit("comesTo", v("x"), v("g")),
|
|
302
|
+
lit("callsInto", v("g"), v("m"), v("n"))
|
|
303
|
+
]
|
|
304
|
+
)
|
|
305
|
+
];
|
|
306
|
+
var ANSWER_RELATIONS = [
|
|
307
|
+
...new Set(RESOLUTION_QUESTIONS.map((r) => r.head.relation))
|
|
308
|
+
];
|
|
174
309
|
export {
|
|
310
|
+
ANSWER_RELATIONS,
|
|
311
|
+
RESOLUTION_QUESTIONS,
|
|
175
312
|
RESOLUTION_RULES
|
|
176
313
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/resolution",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Language-neutral rules for following a value to the function it comes down to.",
|
|
6
6
|
"keywords": [
|
|
@@ -53,6 +53,6 @@
|
|
|
53
53
|
"access": "public"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@suss/datalog": "0.1.
|
|
56
|
+
"@suss/datalog": "0.1.1"
|
|
57
57
|
}
|
|
58
58
|
}
|