@suss/resolution 0.19.0 → 0.21.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 +53 -0
- package/dist/index.cjs +321 -9
- package/dist/index.d.cts +52 -1
- package/dist/index.d.ts +52 -1
- package/dist/index.js +316 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -173,6 +173,59 @@ than a copy of every other. It differs from `callsFunction`, which
|
|
|
173
173
|
starts from the function because a caller asking for call sites has the
|
|
174
174
|
function in hand.
|
|
175
175
|
|
|
176
|
+
## The anchor behind a receiver
|
|
177
|
+
|
|
178
|
+
A pack sometimes wants a call handed back, not a yes or no. Mongoose is
|
|
179
|
+
the picture: `model("User", schema)` is the anchor, and the pack reads
|
|
180
|
+
the model name and the collection off that call's own arguments. The
|
|
181
|
+
receiver in front of a matched method can be the model, a construction
|
|
182
|
+
of it (`new User({...})`), or a document a query returned
|
|
183
|
+
(`await User.findById(id)`), and each is a different number of hops
|
|
184
|
+
from the anchor.
|
|
185
|
+
|
|
186
|
+
`anchorChain` is the reachability that covers all three. From an asked
|
|
187
|
+
value (`wantedAnchor`) it follows names (`binds`, `endsHolding`,
|
|
188
|
+
`fallbackBranch`), imports through the export table, a call to its
|
|
189
|
+
callee, and a method's callee to its receiver. Every call the chain
|
|
190
|
+
passes lands in `wantedAnchorCall`, keyed by the asked value.
|
|
191
|
+
|
|
192
|
+
Candidates, not one answer: the chain has no way to rank a nearer call
|
|
193
|
+
above a farther one, and ranking inside the rules would be a depth
|
|
194
|
+
bound in a new spelling. The asking side filters the candidates against
|
|
195
|
+
its own origin (which module, which callee name) and applies the
|
|
196
|
+
single-answer policy: exactly one distinct match is the anchor, none or
|
|
197
|
+
several is a refusal.
|
|
198
|
+
|
|
199
|
+
The demand cone stays on the base facts named above. Nothing here pulls
|
|
200
|
+
`reaches` or `callsInto`, so asking about a receiver does not price in
|
|
201
|
+
call-graph closure.
|
|
202
|
+
|
|
203
|
+
## The subject behind a registration
|
|
204
|
+
|
|
205
|
+
Discovery keeps asking one question: is this receiver the app, the
|
|
206
|
+
router, the thing built by calling what a library exports? Which
|
|
207
|
+
`.get(...)` calls are routes and which `.use(...)` calls are mounts
|
|
208
|
+
both hang on the answer, and every language feature that moves a value
|
|
209
|
+
is a place the receiver can be written: a class field, a destructured
|
|
210
|
+
name, a property on an object another file built. Enumerating those
|
|
211
|
+
spellings in a walker loses to the language, so the question is asked
|
|
212
|
+
of the rules instead.
|
|
213
|
+
|
|
214
|
+
The answer comes back in two relations, both seeded by
|
|
215
|
+
`wantedSubject`.
|
|
216
|
+
`wantedSubjectWritten` is the written-value walk from the asked
|
|
217
|
+
receiver, so the asking side can apply the single-answer policy over
|
|
218
|
+
everything the receiver could be. `wantedSubjectConstruction` keeps
|
|
219
|
+
only the answers that are a call or a `new`, and pairs each with where
|
|
220
|
+
its callee was imported from, through however many aliases the callee
|
|
221
|
+
went. The asking side checks that pair against the (module, name) a
|
|
222
|
+
pack declares: exactly one distinct construction with a matching
|
|
223
|
+
origin is the subject, none or several is a refusal.
|
|
224
|
+
|
|
225
|
+
The construction end needs no fact of its own. Every call is already a
|
|
226
|
+
`call` fact, a `new` expression included, and the join against
|
|
227
|
+
`comesFrom` is what makes one of them a subject seed.
|
|
228
|
+
|
|
176
229
|
## Explaining an answer
|
|
177
230
|
|
|
178
231
|
Evaluate the same rules under `@suss/datalog`'s `witnesses` algebra and
|
package/dist/index.cjs
CHANGED
|
@@ -22,12 +22,17 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ANSWER_RELATIONS: () => ANSWER_RELATIONS,
|
|
24
24
|
FACT_CONTRACT_CASES: () => FACT_CONTRACT_CASES,
|
|
25
|
+
NAMESPACE_IMPORT: () => NAMESPACE_IMPORT,
|
|
26
|
+
NAMESPACE_IMPORT_NAME: () => NAMESPACE_IMPORT_NAME,
|
|
25
27
|
RESOLUTION_QUESTIONS: () => RESOLUTION_QUESTIONS,
|
|
26
28
|
RESOLUTION_RULES: () => RESOLUTION_RULES,
|
|
27
29
|
RESULT_STEP: () => RESULT_STEP,
|
|
28
30
|
VALUE_STEP: () => VALUE_STEP,
|
|
31
|
+
agreedMountPrefix: () => agreedMountPrefix,
|
|
29
32
|
checkFactContract: () => checkFactContract,
|
|
30
33
|
explainResolutionProof: () => explainResolutionProof,
|
|
34
|
+
joinMountedPath: () => joinMountedPath,
|
|
35
|
+
mountPathsOf: () => mountPathsOf,
|
|
31
36
|
renderExplanation: () => renderExplanation
|
|
32
37
|
});
|
|
33
38
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -461,9 +466,61 @@ function renderExplanation(explanation, describe) {
|
|
|
461
466
|
return lines;
|
|
462
467
|
}
|
|
463
468
|
|
|
469
|
+
// src/mount.ts
|
|
470
|
+
function joinMountedPath(prefix, path) {
|
|
471
|
+
const trimmed = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
472
|
+
return trimmed === "" ? path : trimmed + path;
|
|
473
|
+
}
|
|
474
|
+
function mountPathsOf(edges, childId) {
|
|
475
|
+
return pathsOf(edges, /* @__PURE__ */ new Map(), childId, /* @__PURE__ */ new Set());
|
|
476
|
+
}
|
|
477
|
+
function agreedMountPrefix(edges, childId) {
|
|
478
|
+
const paths = mountPathsOf(edges, childId);
|
|
479
|
+
if (paths === null) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
const distinct2 = new Set(paths);
|
|
483
|
+
const only = [...distinct2][0];
|
|
484
|
+
return distinct2.size === 1 && only !== void 0 ? only : null;
|
|
485
|
+
}
|
|
486
|
+
function pathsOf(edges, memo, childId, visiting) {
|
|
487
|
+
const cached = memo.get(childId);
|
|
488
|
+
if (cached !== void 0) {
|
|
489
|
+
return cached;
|
|
490
|
+
}
|
|
491
|
+
if (visiting.has(childId)) {
|
|
492
|
+
memo.set(childId, null);
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
const own = edges.get(childId);
|
|
496
|
+
if (own === void 0 || own.length === 0) {
|
|
497
|
+
memo.set(childId, [""]);
|
|
498
|
+
return [""];
|
|
499
|
+
}
|
|
500
|
+
visiting.add(childId);
|
|
501
|
+
const composed = /* @__PURE__ */ new Set();
|
|
502
|
+
let unstated = false;
|
|
503
|
+
for (const edge of own) {
|
|
504
|
+
const parentPaths = pathsOf(edges, memo, edge.parentId, visiting);
|
|
505
|
+
if (parentPaths === null) {
|
|
506
|
+
unstated = true;
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
for (const parentPath of parentPaths) {
|
|
510
|
+
composed.add(joinMountedPath(parentPath, edge.prefix));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
visiting.delete(childId);
|
|
514
|
+
const result = unstated ? null : [...composed];
|
|
515
|
+
memo.set(childId, result);
|
|
516
|
+
return result;
|
|
517
|
+
}
|
|
518
|
+
|
|
464
519
|
// src/index.ts
|
|
465
520
|
var import_datalog2 = require("@suss/datalog");
|
|
466
521
|
var VALUE_STEP = (0, import_datalog2.constant)("value");
|
|
522
|
+
var NAMESPACE_IMPORT_NAME = "*";
|
|
523
|
+
var NAMESPACE_IMPORT = (0, import_datalog2.constant)(NAMESPACE_IMPORT_NAME);
|
|
467
524
|
var RESULT_STEP = (0, import_datalog2.constant)("result");
|
|
468
525
|
var RESOLUTION_RULES = [
|
|
469
526
|
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
@@ -683,6 +740,21 @@ var RESOLUTION_RULES = [
|
|
|
683
740
|
(0, import_datalog2.lit)("callKeywordArg", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("a"))
|
|
684
741
|
]
|
|
685
742
|
),
|
|
743
|
+
// Which module's export a re-exported name forwards to, however
|
|
744
|
+
// many barrels deep the forwarding runs.
|
|
745
|
+
(0, import_datalog2.rule)(
|
|
746
|
+
"moduleForwards",
|
|
747
|
+
[(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")],
|
|
748
|
+
[(0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"))]
|
|
749
|
+
),
|
|
750
|
+
(0, import_datalog2.rule)(
|
|
751
|
+
"moduleForwards",
|
|
752
|
+
[(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m3"), (0, import_datalog2.variable)("n3")],
|
|
753
|
+
[
|
|
754
|
+
(0, import_datalog2.lit)("reExports", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")),
|
|
755
|
+
(0, import_datalog2.lit)("moduleForwards", (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"), (0, import_datalog2.variable)("m3"), (0, import_datalog2.variable)("n3"))
|
|
756
|
+
]
|
|
757
|
+
),
|
|
686
758
|
// What a module exports: directly, or through re-export chains.
|
|
687
759
|
(0, import_datalog2.rule)(
|
|
688
760
|
"moduleExport",
|
|
@@ -862,15 +934,9 @@ var RESOLUTION_RULES = [
|
|
|
862
934
|
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
863
935
|
]
|
|
864
936
|
),
|
|
865
|
-
// Calling f ends up calling the name n that module m exports
|
|
866
|
-
//
|
|
867
|
-
//
|
|
868
|
-
// with what the innermost one reaches.
|
|
869
|
-
//
|
|
870
|
-
// Several answers for one function is the normal case, not an
|
|
871
|
-
// ambiguity: a project decorator that composes two library decorators
|
|
872
|
-
// applies both. A caller asks whether a particular (m, n) is among
|
|
873
|
-
// them.
|
|
937
|
+
// Calling f ends up calling the name n that module m exports, one
|
|
938
|
+
// hop through f's own body or deeper through a wrapper of a wrapper.
|
|
939
|
+
// Several results is normal: a composed decorator applies them all.
|
|
874
940
|
(0, import_datalog2.rule)(
|
|
875
941
|
"callsInto",
|
|
876
942
|
[(0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
@@ -938,6 +1004,155 @@ var RESOLUTION_QUESTIONS = [
|
|
|
938
1004
|
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
939
1005
|
[(0, import_datalog2.lit)("wantedOrigin", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))]
|
|
940
1006
|
),
|
|
1007
|
+
// Call-origin questions, for attribution, in their own demand
|
|
1008
|
+
// class. Attribution stops at the import declaration, so nothing
|
|
1009
|
+
// here demands reaches or callsInto. The chains are closures with
|
|
1010
|
+
// no depth bound: an alias run of any length resolves.
|
|
1011
|
+
(0, import_datalog2.rule)("callOriginChain", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("wantedCallOrigin", (0, import_datalog2.variable)("x"))]),
|
|
1012
|
+
(0, import_datalog2.rule)(
|
|
1013
|
+
"callOriginChain",
|
|
1014
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1015
|
+
[(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
|
|
1016
|
+
),
|
|
1017
|
+
(0, import_datalog2.rule)(
|
|
1018
|
+
"callOriginChain",
|
|
1019
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1020
|
+
[
|
|
1021
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1022
|
+
(0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
|
|
1023
|
+
]
|
|
1024
|
+
),
|
|
1025
|
+
(0, import_datalog2.rule)(
|
|
1026
|
+
"callOriginChain",
|
|
1027
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1028
|
+
[
|
|
1029
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1030
|
+
(0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
|
|
1031
|
+
]
|
|
1032
|
+
),
|
|
1033
|
+
// The chain with at least one hop taken, which is what tells a
|
|
1034
|
+
// destructured binding apart from asking about a node directly.
|
|
1035
|
+
(0, import_datalog2.rule)(
|
|
1036
|
+
"callOriginChainStepped",
|
|
1037
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1038
|
+
[(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
|
|
1039
|
+
),
|
|
1040
|
+
(0, import_datalog2.rule)(
|
|
1041
|
+
"callOriginChainStepped",
|
|
1042
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1043
|
+
[
|
|
1044
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1045
|
+
(0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
|
|
1046
|
+
]
|
|
1047
|
+
),
|
|
1048
|
+
(0, import_datalog2.rule)(
|
|
1049
|
+
"callOriginChainStepped",
|
|
1050
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1051
|
+
[
|
|
1052
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1053
|
+
(0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
|
|
1054
|
+
]
|
|
1055
|
+
),
|
|
1056
|
+
(0, import_datalog2.rule)(
|
|
1057
|
+
"wantedCallOriginPair",
|
|
1058
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
1059
|
+
[
|
|
1060
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1061
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
1062
|
+
]
|
|
1063
|
+
),
|
|
1064
|
+
// An import of a project barrel is an import of what the barrel
|
|
1065
|
+
// forwards, so the pair surfaces the module behind the chain.
|
|
1066
|
+
(0, import_datalog2.rule)(
|
|
1067
|
+
"wantedCallOriginPair",
|
|
1068
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2")],
|
|
1069
|
+
[
|
|
1070
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1071
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
|
|
1072
|
+
(0, import_datalog2.lit)("moduleForwards", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("m2"), (0, import_datalog2.variable)("n2"))
|
|
1073
|
+
]
|
|
1074
|
+
),
|
|
1075
|
+
// A namespace member: the module's export of the member's own name.
|
|
1076
|
+
// The imports literal leads, since it is the smallest relation.
|
|
1077
|
+
(0, import_datalog2.rule)(
|
|
1078
|
+
"wantedCallOriginPair",
|
|
1079
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("p")],
|
|
1080
|
+
[
|
|
1081
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("m"), NAMESPACE_IMPORT),
|
|
1082
|
+
(0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("p")),
|
|
1083
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))
|
|
1084
|
+
]
|
|
1085
|
+
),
|
|
1086
|
+
(0, import_datalog2.rule)(
|
|
1087
|
+
"wantedCallOriginPair",
|
|
1088
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("p")],
|
|
1089
|
+
[
|
|
1090
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("d"), (0, import_datalog2.variable)("m"), NAMESPACE_IMPORT),
|
|
1091
|
+
(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("d")),
|
|
1092
|
+
(0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("p")),
|
|
1093
|
+
(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"))
|
|
1094
|
+
]
|
|
1095
|
+
),
|
|
1096
|
+
// What a call made: through the call to its callee, whose own alias
|
|
1097
|
+
// chain leads to the import.
|
|
1098
|
+
(0, import_datalog2.rule)(
|
|
1099
|
+
"callMadeChain",
|
|
1100
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("f")],
|
|
1101
|
+
[(0, import_datalog2.lit)("callOriginChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("c")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))]
|
|
1102
|
+
),
|
|
1103
|
+
(0, import_datalog2.rule)(
|
|
1104
|
+
"callMadeChain",
|
|
1105
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1106
|
+
[(0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
|
|
1107
|
+
),
|
|
1108
|
+
(0, import_datalog2.rule)(
|
|
1109
|
+
"callMadeChain",
|
|
1110
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1111
|
+
[(0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
|
|
1112
|
+
),
|
|
1113
|
+
(0, import_datalog2.rule)(
|
|
1114
|
+
"wantedCallOriginPair",
|
|
1115
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
1116
|
+
[
|
|
1117
|
+
(0, import_datalog2.lit)("callMadeChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1118
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
1119
|
+
]
|
|
1120
|
+
),
|
|
1121
|
+
// A member destructured off what a call made keeps the member name
|
|
1122
|
+
// as one more path segment.
|
|
1123
|
+
(0, import_datalog2.rule)(
|
|
1124
|
+
"callMemberChain",
|
|
1125
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("p")],
|
|
1126
|
+
[
|
|
1127
|
+
(0, import_datalog2.lit)("callOriginChainStepped", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("e")),
|
|
1128
|
+
(0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("e"), (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("p")),
|
|
1129
|
+
(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("f"))
|
|
1130
|
+
]
|
|
1131
|
+
),
|
|
1132
|
+
(0, import_datalog2.rule)(
|
|
1133
|
+
"callMemberChain",
|
|
1134
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("p")],
|
|
1135
|
+
[
|
|
1136
|
+
(0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
|
|
1137
|
+
(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
|
|
1138
|
+
]
|
|
1139
|
+
),
|
|
1140
|
+
(0, import_datalog2.rule)(
|
|
1141
|
+
"callMemberChain",
|
|
1142
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z"), (0, import_datalog2.variable)("p")],
|
|
1143
|
+
[
|
|
1144
|
+
(0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
|
|
1145
|
+
(0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))
|
|
1146
|
+
]
|
|
1147
|
+
),
|
|
1148
|
+
(0, import_datalog2.rule)(
|
|
1149
|
+
"wantedCallOriginMember",
|
|
1150
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("p")],
|
|
1151
|
+
[
|
|
1152
|
+
(0, import_datalog2.lit)("callMemberChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("p")),
|
|
1153
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
1154
|
+
]
|
|
1155
|
+
),
|
|
941
1156
|
(0, import_datalog2.rule)(
|
|
942
1157
|
"wantedCallsInto",
|
|
943
1158
|
[(0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
@@ -946,6 +1161,98 @@ var RESOLUTION_QUESTIONS = [
|
|
|
946
1161
|
(0, import_datalog2.lit)("comesTo", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("g")),
|
|
947
1162
|
(0, import_datalog2.lit)("callsInto", (0, import_datalog2.variable)("g"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
948
1163
|
]
|
|
1164
|
+
),
|
|
1165
|
+
// Subject identity, its own demand class; the README's subject
|
|
1166
|
+
// section says why the origin pair rides on the written-value walk.
|
|
1167
|
+
(0, import_datalog2.rule)(
|
|
1168
|
+
"wantedSubjectWritten",
|
|
1169
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")],
|
|
1170
|
+
[(0, import_datalog2.lit)("wantedSubject", (0, import_datalog2.variable)("x")), (0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r"))]
|
|
1171
|
+
),
|
|
1172
|
+
(0, import_datalog2.rule)(
|
|
1173
|
+
"wantedSubjectConstruction",
|
|
1174
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")],
|
|
1175
|
+
[
|
|
1176
|
+
(0, import_datalog2.lit)("wantedSubject", (0, import_datalog2.variable)("x")),
|
|
1177
|
+
(0, import_datalog2.lit)("isWrittenAs", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")),
|
|
1178
|
+
(0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c")),
|
|
1179
|
+
(0, import_datalog2.lit)("comesFrom", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"))
|
|
1180
|
+
]
|
|
1181
|
+
),
|
|
1182
|
+
// The export table of one module, keyed by the module asked about.
|
|
1183
|
+
// The moduleExport rules flatten re-export chains of any length, and
|
|
1184
|
+
// the demand cone stays on export facts alone.
|
|
1185
|
+
(0, import_datalog2.rule)(
|
|
1186
|
+
"wantedModuleExport",
|
|
1187
|
+
[(0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value")],
|
|
1188
|
+
[
|
|
1189
|
+
(0, import_datalog2.lit)("wantedExportsOf", (0, import_datalog2.variable)("m")),
|
|
1190
|
+
(0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("value"))
|
|
1191
|
+
]
|
|
1192
|
+
),
|
|
1193
|
+
// The calls behind a receiver, for a pack that wants the anchor a
|
|
1194
|
+
// chain hangs off; the README's anchor section says which hops and
|
|
1195
|
+
// why the asking side applies the single-answer policy.
|
|
1196
|
+
// A class's ancestry, one hop per extends through the binding that
|
|
1197
|
+
// says which class the written base is; the names on the way out
|
|
1198
|
+
// are what a storage pack matches its library's bases against.
|
|
1199
|
+
(0, import_datalog2.rule)("ancestryChain", [(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("c")], [(0, import_datalog2.lit)("wantedAncestry", (0, import_datalog2.variable)("c"))]),
|
|
1200
|
+
(0, import_datalog2.rule)(
|
|
1201
|
+
"ancestryChain",
|
|
1202
|
+
[(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b2")],
|
|
1203
|
+
[
|
|
1204
|
+
(0, import_datalog2.lit)("ancestryChain", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b")),
|
|
1205
|
+
(0, import_datalog2.lit)("extends", (0, import_datalog2.variable)("b"), (0, import_datalog2.variable)("x")),
|
|
1206
|
+
(0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("b2"))
|
|
1207
|
+
]
|
|
1208
|
+
),
|
|
1209
|
+
(0, import_datalog2.rule)(
|
|
1210
|
+
"wantedBaseName",
|
|
1211
|
+
[(0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("n")],
|
|
1212
|
+
[(0, import_datalog2.lit)("ancestryChain", (0, import_datalog2.variable)("c"), (0, import_datalog2.variable)("b")), (0, import_datalog2.lit)("extendsNamed", (0, import_datalog2.variable)("b"), (0, import_datalog2.variable)("n"))]
|
|
1213
|
+
),
|
|
1214
|
+
(0, import_datalog2.rule)("anchorChain", [(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("x")], [(0, import_datalog2.lit)("wantedAnchor", (0, import_datalog2.variable)("x"))]),
|
|
1215
|
+
(0, import_datalog2.rule)(
|
|
1216
|
+
"anchorChain",
|
|
1217
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1218
|
+
[(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("binds", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
|
|
1219
|
+
),
|
|
1220
|
+
(0, import_datalog2.rule)(
|
|
1221
|
+
"anchorChain",
|
|
1222
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1223
|
+
[(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("endsHolding", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
|
|
1224
|
+
),
|
|
1225
|
+
(0, import_datalog2.rule)(
|
|
1226
|
+
"anchorChain",
|
|
1227
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1228
|
+
[(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")), (0, import_datalog2.lit)("fallbackBranch", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("z"))]
|
|
1229
|
+
),
|
|
1230
|
+
(0, import_datalog2.rule)(
|
|
1231
|
+
"anchorChain",
|
|
1232
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("z")],
|
|
1233
|
+
[
|
|
1234
|
+
(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1235
|
+
(0, import_datalog2.lit)("imports", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n")),
|
|
1236
|
+
(0, import_datalog2.lit)("moduleExport", (0, import_datalog2.variable)("m"), (0, import_datalog2.variable)("n"), (0, import_datalog2.variable)("z"))
|
|
1237
|
+
]
|
|
1238
|
+
),
|
|
1239
|
+
(0, import_datalog2.rule)(
|
|
1240
|
+
"anchorChain",
|
|
1241
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("c")],
|
|
1242
|
+
[(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
|
|
1243
|
+
),
|
|
1244
|
+
(0, import_datalog2.rule)(
|
|
1245
|
+
"anchorChain",
|
|
1246
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("o")],
|
|
1247
|
+
[
|
|
1248
|
+
(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("y")),
|
|
1249
|
+
(0, import_datalog2.lit)("readsProperty", (0, import_datalog2.variable)("y"), (0, import_datalog2.variable)("o"), (0, import_datalog2.variable)("n"))
|
|
1250
|
+
]
|
|
1251
|
+
),
|
|
1252
|
+
(0, import_datalog2.rule)(
|
|
1253
|
+
"wantedAnchorCall",
|
|
1254
|
+
[(0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")],
|
|
1255
|
+
[(0, import_datalog2.lit)("anchorChain", (0, import_datalog2.variable)("x"), (0, import_datalog2.variable)("r")), (0, import_datalog2.lit)("call", (0, import_datalog2.variable)("r"), (0, import_datalog2.variable)("c"))]
|
|
949
1256
|
)
|
|
950
1257
|
];
|
|
951
1258
|
var ANSWER_RELATIONS = [
|
|
@@ -955,11 +1262,16 @@ var ANSWER_RELATIONS = [
|
|
|
955
1262
|
0 && (module.exports = {
|
|
956
1263
|
ANSWER_RELATIONS,
|
|
957
1264
|
FACT_CONTRACT_CASES,
|
|
1265
|
+
NAMESPACE_IMPORT,
|
|
1266
|
+
NAMESPACE_IMPORT_NAME,
|
|
958
1267
|
RESOLUTION_QUESTIONS,
|
|
959
1268
|
RESOLUTION_RULES,
|
|
960
1269
|
RESULT_STEP,
|
|
961
1270
|
VALUE_STEP,
|
|
1271
|
+
agreedMountPrefix,
|
|
962
1272
|
checkFactContract,
|
|
963
1273
|
explainResolutionProof,
|
|
1274
|
+
joinMountedPath,
|
|
1275
|
+
mountPathsOf,
|
|
964
1276
|
renderExplanation
|
|
965
1277
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -105,8 +105,59 @@ declare function explainResolutionProof(proof: Proof, options: ExplainOptions):
|
|
|
105
105
|
*/
|
|
106
106
|
declare function renderExplanation(explanation: ResolutionExplanation, describe: DescribeAtom): string[];
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Mount composition, shared between adapters. An adapter states one
|
|
110
|
+
* fact per mount call, `mounted(child, parent, prefix)`, however its
|
|
111
|
+
* language spells the call: Express's `app.use(prefix, router)`,
|
|
112
|
+
* FastAPI's `app.include_router(router, prefix=...)`. This module
|
|
113
|
+
* composes those edges into the full prefix a child's routes are
|
|
114
|
+
* served under, so both adapters compute the same paths.
|
|
115
|
+
*
|
|
116
|
+
* The composition is a function over the edge set rather than a
|
|
117
|
+
* datalog rule because a rule head cannot build a new atom, and a
|
|
118
|
+
* path-concatenating closure would not terminate on a mount cycle:
|
|
119
|
+
* the string grows on every round. Here a cycle is an abstention.
|
|
120
|
+
*/
|
|
121
|
+
interface MountEdge {
|
|
122
|
+
parentId: string;
|
|
123
|
+
prefix: string;
|
|
124
|
+
}
|
|
125
|
+
/** Every mount edge, keyed by the mounted child. */
|
|
126
|
+
type MountEdges = ReadonlyMap<string, readonly MountEdge[]>;
|
|
127
|
+
/**
|
|
128
|
+
* Join a mount prefix onto a path written with its own leading slash.
|
|
129
|
+
* A root prefix strips to nothing, so mounting under "/" leaves the
|
|
130
|
+
* path as written.
|
|
131
|
+
*/
|
|
132
|
+
declare function joinMountedPath(prefix: string, path: string): string;
|
|
133
|
+
/**
|
|
134
|
+
* Every full prefix the child's mount chains compose to, one per
|
|
135
|
+
* mount, which is what one-boundary-per-mount reads. Null when a
|
|
136
|
+
* chain cannot be stated: a cycle, or an ancestor whose own paths
|
|
137
|
+
* cannot be stated. An unmounted child composes to [""].
|
|
138
|
+
*/
|
|
139
|
+
declare function mountPathsOf(edges: MountEdges, childId: string): readonly string[] | null;
|
|
140
|
+
/**
|
|
141
|
+
* The one prefix every mount chain agrees on. "" for an unmounted
|
|
142
|
+
* child, null when a cycle or a disagreement leaves nothing to say.
|
|
143
|
+
*
|
|
144
|
+
* Agreement is checked on the composed result, not the literal prefix
|
|
145
|
+
* a mount call gives: two mounts spelling the identical local prefix
|
|
146
|
+
* still land at different full paths when one parent is itself
|
|
147
|
+
* mounted somewhere the other is not.
|
|
148
|
+
*/
|
|
149
|
+
declare function agreedMountPrefix(edges: MountEdges, childId: string): string | null;
|
|
150
|
+
|
|
108
151
|
/** A step to the value x is written as. */
|
|
109
152
|
declare const VALUE_STEP: _suss_datalog.Term;
|
|
153
|
+
/**
|
|
154
|
+
* The name a whole-module import records itself under, whatever the
|
|
155
|
+
* language spells it as: `import * as ns` in TypeScript, `import
|
|
156
|
+
* module` in Python. Adapters emit this name; the member-read rule
|
|
157
|
+
* joins on it.
|
|
158
|
+
*/
|
|
159
|
+
declare const NAMESPACE_IMPORT_NAME = "*";
|
|
160
|
+
declare const NAMESPACE_IMPORT: _suss_datalog.Term;
|
|
110
161
|
/** A step to what running the call x is handed back. */
|
|
111
162
|
declare const RESULT_STEP: _suss_datalog.Term;
|
|
112
163
|
/**
|
|
@@ -145,4 +196,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
|
|
|
145
196
|
/** The relations `RESOLUTION_QUESTIONS` answers into. */
|
|
146
197
|
declare const ANSWER_RELATIONS: string[];
|
|
147
198
|
|
|
148
|
-
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, checkFactContract, explainResolutionProof, renderExplanation };
|
|
199
|
+
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, agreedMountPrefix, checkFactContract, explainResolutionProof, joinMountedPath, mountPathsOf, renderExplanation };
|
package/dist/index.d.ts
CHANGED
|
@@ -105,8 +105,59 @@ declare function explainResolutionProof(proof: Proof, options: ExplainOptions):
|
|
|
105
105
|
*/
|
|
106
106
|
declare function renderExplanation(explanation: ResolutionExplanation, describe: DescribeAtom): string[];
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Mount composition, shared between adapters. An adapter states one
|
|
110
|
+
* fact per mount call, `mounted(child, parent, prefix)`, however its
|
|
111
|
+
* language spells the call: Express's `app.use(prefix, router)`,
|
|
112
|
+
* FastAPI's `app.include_router(router, prefix=...)`. This module
|
|
113
|
+
* composes those edges into the full prefix a child's routes are
|
|
114
|
+
* served under, so both adapters compute the same paths.
|
|
115
|
+
*
|
|
116
|
+
* The composition is a function over the edge set rather than a
|
|
117
|
+
* datalog rule because a rule head cannot build a new atom, and a
|
|
118
|
+
* path-concatenating closure would not terminate on a mount cycle:
|
|
119
|
+
* the string grows on every round. Here a cycle is an abstention.
|
|
120
|
+
*/
|
|
121
|
+
interface MountEdge {
|
|
122
|
+
parentId: string;
|
|
123
|
+
prefix: string;
|
|
124
|
+
}
|
|
125
|
+
/** Every mount edge, keyed by the mounted child. */
|
|
126
|
+
type MountEdges = ReadonlyMap<string, readonly MountEdge[]>;
|
|
127
|
+
/**
|
|
128
|
+
* Join a mount prefix onto a path written with its own leading slash.
|
|
129
|
+
* A root prefix strips to nothing, so mounting under "/" leaves the
|
|
130
|
+
* path as written.
|
|
131
|
+
*/
|
|
132
|
+
declare function joinMountedPath(prefix: string, path: string): string;
|
|
133
|
+
/**
|
|
134
|
+
* Every full prefix the child's mount chains compose to, one per
|
|
135
|
+
* mount, which is what one-boundary-per-mount reads. Null when a
|
|
136
|
+
* chain cannot be stated: a cycle, or an ancestor whose own paths
|
|
137
|
+
* cannot be stated. An unmounted child composes to [""].
|
|
138
|
+
*/
|
|
139
|
+
declare function mountPathsOf(edges: MountEdges, childId: string): readonly string[] | null;
|
|
140
|
+
/**
|
|
141
|
+
* The one prefix every mount chain agrees on. "" for an unmounted
|
|
142
|
+
* child, null when a cycle or a disagreement leaves nothing to say.
|
|
143
|
+
*
|
|
144
|
+
* Agreement is checked on the composed result, not the literal prefix
|
|
145
|
+
* a mount call gives: two mounts spelling the identical local prefix
|
|
146
|
+
* still land at different full paths when one parent is itself
|
|
147
|
+
* mounted somewhere the other is not.
|
|
148
|
+
*/
|
|
149
|
+
declare function agreedMountPrefix(edges: MountEdges, childId: string): string | null;
|
|
150
|
+
|
|
108
151
|
/** A step to the value x is written as. */
|
|
109
152
|
declare const VALUE_STEP: _suss_datalog.Term;
|
|
153
|
+
/**
|
|
154
|
+
* The name a whole-module import records itself under, whatever the
|
|
155
|
+
* language spells it as: `import * as ns` in TypeScript, `import
|
|
156
|
+
* module` in Python. Adapters emit this name; the member-read rule
|
|
157
|
+
* joins on it.
|
|
158
|
+
*/
|
|
159
|
+
declare const NAMESPACE_IMPORT_NAME = "*";
|
|
160
|
+
declare const NAMESPACE_IMPORT: _suss_datalog.Term;
|
|
110
161
|
/** A step to what running the call x is handed back. */
|
|
111
162
|
declare const RESULT_STEP: _suss_datalog.Term;
|
|
112
163
|
/**
|
|
@@ -145,4 +196,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
|
|
|
145
196
|
/** The relations `RESOLUTION_QUESTIONS` answers into. */
|
|
146
197
|
declare const ANSWER_RELATIONS: string[];
|
|
147
198
|
|
|
148
|
-
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, checkFactContract, explainResolutionProof, renderExplanation };
|
|
199
|
+
export { ANSWER_RELATIONS, type CaseFiles, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ResolutionExplanation, type ResolutionStep, type StepContext, type StepPhrase, VALUE_STEP, agreedMountPrefix, checkFactContract, explainResolutionProof, joinMountedPath, mountPathsOf, renderExplanation };
|
package/dist/index.js
CHANGED
|
@@ -427,9 +427,61 @@ function renderExplanation(explanation, describe) {
|
|
|
427
427
|
return lines;
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
// src/mount.ts
|
|
431
|
+
function joinMountedPath(prefix, path) {
|
|
432
|
+
const trimmed = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
433
|
+
return trimmed === "" ? path : trimmed + path;
|
|
434
|
+
}
|
|
435
|
+
function mountPathsOf(edges, childId) {
|
|
436
|
+
return pathsOf(edges, /* @__PURE__ */ new Map(), childId, /* @__PURE__ */ new Set());
|
|
437
|
+
}
|
|
438
|
+
function agreedMountPrefix(edges, childId) {
|
|
439
|
+
const paths = mountPathsOf(edges, childId);
|
|
440
|
+
if (paths === null) {
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
const distinct2 = new Set(paths);
|
|
444
|
+
const only = [...distinct2][0];
|
|
445
|
+
return distinct2.size === 1 && only !== void 0 ? only : null;
|
|
446
|
+
}
|
|
447
|
+
function pathsOf(edges, memo, childId, visiting) {
|
|
448
|
+
const cached = memo.get(childId);
|
|
449
|
+
if (cached !== void 0) {
|
|
450
|
+
return cached;
|
|
451
|
+
}
|
|
452
|
+
if (visiting.has(childId)) {
|
|
453
|
+
memo.set(childId, null);
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
const own = edges.get(childId);
|
|
457
|
+
if (own === void 0 || own.length === 0) {
|
|
458
|
+
memo.set(childId, [""]);
|
|
459
|
+
return [""];
|
|
460
|
+
}
|
|
461
|
+
visiting.add(childId);
|
|
462
|
+
const composed = /* @__PURE__ */ new Set();
|
|
463
|
+
let unstated = false;
|
|
464
|
+
for (const edge of own) {
|
|
465
|
+
const parentPaths = pathsOf(edges, memo, edge.parentId, visiting);
|
|
466
|
+
if (parentPaths === null) {
|
|
467
|
+
unstated = true;
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
for (const parentPath of parentPaths) {
|
|
471
|
+
composed.add(joinMountedPath(parentPath, edge.prefix));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
visiting.delete(childId);
|
|
475
|
+
const result = unstated ? null : [...composed];
|
|
476
|
+
memo.set(childId, result);
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
|
|
430
480
|
// src/index.ts
|
|
431
481
|
import { constant, lit, rule, variable as v } from "@suss/datalog";
|
|
432
482
|
var VALUE_STEP = constant("value");
|
|
483
|
+
var NAMESPACE_IMPORT_NAME = "*";
|
|
484
|
+
var NAMESPACE_IMPORT = constant(NAMESPACE_IMPORT_NAME);
|
|
433
485
|
var RESULT_STEP = constant("result");
|
|
434
486
|
var RESOLUTION_RULES = [
|
|
435
487
|
// Aliasing: const x = y, or an identifier referencing a declaration.
|
|
@@ -649,6 +701,21 @@ var RESOLUTION_RULES = [
|
|
|
649
701
|
lit("callKeywordArg", v("r"), v("n"), v("a"))
|
|
650
702
|
]
|
|
651
703
|
),
|
|
704
|
+
// Which module's export a re-exported name forwards to, however
|
|
705
|
+
// many barrels deep the forwarding runs.
|
|
706
|
+
rule(
|
|
707
|
+
"moduleForwards",
|
|
708
|
+
[v("m"), v("n"), v("m2"), v("n2")],
|
|
709
|
+
[lit("reExports", v("m"), v("n"), v("m2"), v("n2"))]
|
|
710
|
+
),
|
|
711
|
+
rule(
|
|
712
|
+
"moduleForwards",
|
|
713
|
+
[v("m"), v("n"), v("m3"), v("n3")],
|
|
714
|
+
[
|
|
715
|
+
lit("reExports", v("m"), v("n"), v("m2"), v("n2")),
|
|
716
|
+
lit("moduleForwards", v("m2"), v("n2"), v("m3"), v("n3"))
|
|
717
|
+
]
|
|
718
|
+
),
|
|
652
719
|
// What a module exports: directly, or through re-export chains.
|
|
653
720
|
rule(
|
|
654
721
|
"moduleExport",
|
|
@@ -828,15 +895,9 @@ var RESOLUTION_RULES = [
|
|
|
828
895
|
lit("imports", v("y"), v("m"), v("n"))
|
|
829
896
|
]
|
|
830
897
|
),
|
|
831
|
-
// Calling f ends up calling the name n that module m exports
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
// with what the innermost one reaches.
|
|
835
|
-
//
|
|
836
|
-
// Several answers for one function is the normal case, not an
|
|
837
|
-
// ambiguity: a project decorator that composes two library decorators
|
|
838
|
-
// applies both. A caller asks whether a particular (m, n) is among
|
|
839
|
-
// them.
|
|
898
|
+
// Calling f ends up calling the name n that module m exports, one
|
|
899
|
+
// hop through f's own body or deeper through a wrapper of a wrapper.
|
|
900
|
+
// Several results is normal: a composed decorator applies them all.
|
|
840
901
|
rule(
|
|
841
902
|
"callsInto",
|
|
842
903
|
[v("f"), v("m"), v("n")],
|
|
@@ -904,6 +965,155 @@ var RESOLUTION_QUESTIONS = [
|
|
|
904
965
|
[v("x"), v("m"), v("n")],
|
|
905
966
|
[lit("wantedOrigin", v("x")), lit("comesFrom", v("x"), v("m"), v("n"))]
|
|
906
967
|
),
|
|
968
|
+
// Call-origin questions, for attribution, in their own demand
|
|
969
|
+
// class. Attribution stops at the import declaration, so nothing
|
|
970
|
+
// here demands reaches or callsInto. The chains are closures with
|
|
971
|
+
// no depth bound: an alias run of any length resolves.
|
|
972
|
+
rule("callOriginChain", [v("x"), v("x")], [lit("wantedCallOrigin", v("x"))]),
|
|
973
|
+
rule(
|
|
974
|
+
"callOriginChain",
|
|
975
|
+
[v("x"), v("z")],
|
|
976
|
+
[lit("callOriginChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
|
|
977
|
+
),
|
|
978
|
+
rule(
|
|
979
|
+
"callOriginChain",
|
|
980
|
+
[v("x"), v("z")],
|
|
981
|
+
[
|
|
982
|
+
lit("callOriginChain", v("x"), v("y")),
|
|
983
|
+
lit("endsHolding", v("y"), v("z"))
|
|
984
|
+
]
|
|
985
|
+
),
|
|
986
|
+
rule(
|
|
987
|
+
"callOriginChain",
|
|
988
|
+
[v("x"), v("z")],
|
|
989
|
+
[
|
|
990
|
+
lit("callOriginChain", v("x"), v("y")),
|
|
991
|
+
lit("fallbackBranch", v("y"), v("z"))
|
|
992
|
+
]
|
|
993
|
+
),
|
|
994
|
+
// The chain with at least one hop taken, which is what tells a
|
|
995
|
+
// destructured binding apart from asking about a node directly.
|
|
996
|
+
rule(
|
|
997
|
+
"callOriginChainStepped",
|
|
998
|
+
[v("x"), v("z")],
|
|
999
|
+
[lit("callOriginChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
|
|
1000
|
+
),
|
|
1001
|
+
rule(
|
|
1002
|
+
"callOriginChainStepped",
|
|
1003
|
+
[v("x"), v("z")],
|
|
1004
|
+
[
|
|
1005
|
+
lit("callOriginChain", v("x"), v("y")),
|
|
1006
|
+
lit("endsHolding", v("y"), v("z"))
|
|
1007
|
+
]
|
|
1008
|
+
),
|
|
1009
|
+
rule(
|
|
1010
|
+
"callOriginChainStepped",
|
|
1011
|
+
[v("x"), v("z")],
|
|
1012
|
+
[
|
|
1013
|
+
lit("callOriginChain", v("x"), v("y")),
|
|
1014
|
+
lit("fallbackBranch", v("y"), v("z"))
|
|
1015
|
+
]
|
|
1016
|
+
),
|
|
1017
|
+
rule(
|
|
1018
|
+
"wantedCallOriginPair",
|
|
1019
|
+
[v("x"), v("m"), v("n")],
|
|
1020
|
+
[
|
|
1021
|
+
lit("callOriginChain", v("x"), v("y")),
|
|
1022
|
+
lit("imports", v("y"), v("m"), v("n"))
|
|
1023
|
+
]
|
|
1024
|
+
),
|
|
1025
|
+
// An import of a project barrel is an import of what the barrel
|
|
1026
|
+
// forwards, so the pair surfaces the module behind the chain.
|
|
1027
|
+
rule(
|
|
1028
|
+
"wantedCallOriginPair",
|
|
1029
|
+
[v("x"), v("m2"), v("n2")],
|
|
1030
|
+
[
|
|
1031
|
+
lit("callOriginChain", v("x"), v("y")),
|
|
1032
|
+
lit("imports", v("y"), v("m"), v("n")),
|
|
1033
|
+
lit("moduleForwards", v("m"), v("n"), v("m2"), v("n2"))
|
|
1034
|
+
]
|
|
1035
|
+
),
|
|
1036
|
+
// A namespace member: the module's export of the member's own name.
|
|
1037
|
+
// The imports literal leads, since it is the smallest relation.
|
|
1038
|
+
rule(
|
|
1039
|
+
"wantedCallOriginPair",
|
|
1040
|
+
[v("x"), v("m"), v("p")],
|
|
1041
|
+
[
|
|
1042
|
+
lit("imports", v("o"), v("m"), NAMESPACE_IMPORT),
|
|
1043
|
+
lit("readsProperty", v("y"), v("o"), v("p")),
|
|
1044
|
+
lit("callOriginChain", v("x"), v("y"))
|
|
1045
|
+
]
|
|
1046
|
+
),
|
|
1047
|
+
rule(
|
|
1048
|
+
"wantedCallOriginPair",
|
|
1049
|
+
[v("x"), v("m"), v("p")],
|
|
1050
|
+
[
|
|
1051
|
+
lit("imports", v("d"), v("m"), NAMESPACE_IMPORT),
|
|
1052
|
+
lit("binds", v("o"), v("d")),
|
|
1053
|
+
lit("readsProperty", v("y"), v("o"), v("p")),
|
|
1054
|
+
lit("callOriginChain", v("x"), v("y"))
|
|
1055
|
+
]
|
|
1056
|
+
),
|
|
1057
|
+
// What a call made: through the call to its callee, whose own alias
|
|
1058
|
+
// chain leads to the import.
|
|
1059
|
+
rule(
|
|
1060
|
+
"callMadeChain",
|
|
1061
|
+
[v("x"), v("f")],
|
|
1062
|
+
[lit("callOriginChain", v("x"), v("c")), lit("call", v("c"), v("f"))]
|
|
1063
|
+
),
|
|
1064
|
+
rule(
|
|
1065
|
+
"callMadeChain",
|
|
1066
|
+
[v("x"), v("z")],
|
|
1067
|
+
[lit("callMadeChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
|
|
1068
|
+
),
|
|
1069
|
+
rule(
|
|
1070
|
+
"callMadeChain",
|
|
1071
|
+
[v("x"), v("z")],
|
|
1072
|
+
[lit("callMadeChain", v("x"), v("y")), lit("endsHolding", v("y"), v("z"))]
|
|
1073
|
+
),
|
|
1074
|
+
rule(
|
|
1075
|
+
"wantedCallOriginPair",
|
|
1076
|
+
[v("x"), v("m"), v("n")],
|
|
1077
|
+
[
|
|
1078
|
+
lit("callMadeChain", v("x"), v("y")),
|
|
1079
|
+
lit("imports", v("y"), v("m"), v("n"))
|
|
1080
|
+
]
|
|
1081
|
+
),
|
|
1082
|
+
// A member destructured off what a call made keeps the member name
|
|
1083
|
+
// as one more path segment.
|
|
1084
|
+
rule(
|
|
1085
|
+
"callMemberChain",
|
|
1086
|
+
[v("x"), v("f"), v("p")],
|
|
1087
|
+
[
|
|
1088
|
+
lit("callOriginChainStepped", v("x"), v("e")),
|
|
1089
|
+
lit("readsProperty", v("e"), v("c"), v("p")),
|
|
1090
|
+
lit("call", v("c"), v("f"))
|
|
1091
|
+
]
|
|
1092
|
+
),
|
|
1093
|
+
rule(
|
|
1094
|
+
"callMemberChain",
|
|
1095
|
+
[v("x"), v("z"), v("p")],
|
|
1096
|
+
[
|
|
1097
|
+
lit("callMemberChain", v("x"), v("y"), v("p")),
|
|
1098
|
+
lit("binds", v("y"), v("z"))
|
|
1099
|
+
]
|
|
1100
|
+
),
|
|
1101
|
+
rule(
|
|
1102
|
+
"callMemberChain",
|
|
1103
|
+
[v("x"), v("z"), v("p")],
|
|
1104
|
+
[
|
|
1105
|
+
lit("callMemberChain", v("x"), v("y"), v("p")),
|
|
1106
|
+
lit("endsHolding", v("y"), v("z"))
|
|
1107
|
+
]
|
|
1108
|
+
),
|
|
1109
|
+
rule(
|
|
1110
|
+
"wantedCallOriginMember",
|
|
1111
|
+
[v("x"), v("m"), v("n"), v("p")],
|
|
1112
|
+
[
|
|
1113
|
+
lit("callMemberChain", v("x"), v("y"), v("p")),
|
|
1114
|
+
lit("imports", v("y"), v("m"), v("n"))
|
|
1115
|
+
]
|
|
1116
|
+
),
|
|
907
1117
|
rule(
|
|
908
1118
|
"wantedCallsInto",
|
|
909
1119
|
[v("g"), v("m"), v("n")],
|
|
@@ -912,6 +1122,98 @@ var RESOLUTION_QUESTIONS = [
|
|
|
912
1122
|
lit("comesTo", v("x"), v("g")),
|
|
913
1123
|
lit("callsInto", v("g"), v("m"), v("n"))
|
|
914
1124
|
]
|
|
1125
|
+
),
|
|
1126
|
+
// Subject identity, its own demand class; the README's subject
|
|
1127
|
+
// section says why the origin pair rides on the written-value walk.
|
|
1128
|
+
rule(
|
|
1129
|
+
"wantedSubjectWritten",
|
|
1130
|
+
[v("x"), v("r")],
|
|
1131
|
+
[lit("wantedSubject", v("x")), lit("isWrittenAs", v("x"), v("r"))]
|
|
1132
|
+
),
|
|
1133
|
+
rule(
|
|
1134
|
+
"wantedSubjectConstruction",
|
|
1135
|
+
[v("x"), v("r"), v("m"), v("n")],
|
|
1136
|
+
[
|
|
1137
|
+
lit("wantedSubject", v("x")),
|
|
1138
|
+
lit("isWrittenAs", v("x"), v("r")),
|
|
1139
|
+
lit("call", v("r"), v("c")),
|
|
1140
|
+
lit("comesFrom", v("c"), v("m"), v("n"))
|
|
1141
|
+
]
|
|
1142
|
+
),
|
|
1143
|
+
// The export table of one module, keyed by the module asked about.
|
|
1144
|
+
// The moduleExport rules flatten re-export chains of any length, and
|
|
1145
|
+
// the demand cone stays on export facts alone.
|
|
1146
|
+
rule(
|
|
1147
|
+
"wantedModuleExport",
|
|
1148
|
+
[v("m"), v("n"), v("value")],
|
|
1149
|
+
[
|
|
1150
|
+
lit("wantedExportsOf", v("m")),
|
|
1151
|
+
lit("moduleExport", v("m"), v("n"), v("value"))
|
|
1152
|
+
]
|
|
1153
|
+
),
|
|
1154
|
+
// The calls behind a receiver, for a pack that wants the anchor a
|
|
1155
|
+
// chain hangs off; the README's anchor section says which hops and
|
|
1156
|
+
// why the asking side applies the single-answer policy.
|
|
1157
|
+
// A class's ancestry, one hop per extends through the binding that
|
|
1158
|
+
// says which class the written base is; the names on the way out
|
|
1159
|
+
// are what a storage pack matches its library's bases against.
|
|
1160
|
+
rule("ancestryChain", [v("c"), v("c")], [lit("wantedAncestry", v("c"))]),
|
|
1161
|
+
rule(
|
|
1162
|
+
"ancestryChain",
|
|
1163
|
+
[v("c"), v("b2")],
|
|
1164
|
+
[
|
|
1165
|
+
lit("ancestryChain", v("c"), v("b")),
|
|
1166
|
+
lit("extends", v("b"), v("x")),
|
|
1167
|
+
lit("binds", v("x"), v("b2"))
|
|
1168
|
+
]
|
|
1169
|
+
),
|
|
1170
|
+
rule(
|
|
1171
|
+
"wantedBaseName",
|
|
1172
|
+
[v("c"), v("n")],
|
|
1173
|
+
[lit("ancestryChain", v("c"), v("b")), lit("extendsNamed", v("b"), v("n"))]
|
|
1174
|
+
),
|
|
1175
|
+
rule("anchorChain", [v("x"), v("x")], [lit("wantedAnchor", v("x"))]),
|
|
1176
|
+
rule(
|
|
1177
|
+
"anchorChain",
|
|
1178
|
+
[v("x"), v("z")],
|
|
1179
|
+
[lit("anchorChain", v("x"), v("y")), lit("binds", v("y"), v("z"))]
|
|
1180
|
+
),
|
|
1181
|
+
rule(
|
|
1182
|
+
"anchorChain",
|
|
1183
|
+
[v("x"), v("z")],
|
|
1184
|
+
[lit("anchorChain", v("x"), v("y")), lit("endsHolding", v("y"), v("z"))]
|
|
1185
|
+
),
|
|
1186
|
+
rule(
|
|
1187
|
+
"anchorChain",
|
|
1188
|
+
[v("x"), v("z")],
|
|
1189
|
+
[lit("anchorChain", v("x"), v("y")), lit("fallbackBranch", v("y"), v("z"))]
|
|
1190
|
+
),
|
|
1191
|
+
rule(
|
|
1192
|
+
"anchorChain",
|
|
1193
|
+
[v("x"), v("z")],
|
|
1194
|
+
[
|
|
1195
|
+
lit("anchorChain", v("x"), v("y")),
|
|
1196
|
+
lit("imports", v("y"), v("m"), v("n")),
|
|
1197
|
+
lit("moduleExport", v("m"), v("n"), v("z"))
|
|
1198
|
+
]
|
|
1199
|
+
),
|
|
1200
|
+
rule(
|
|
1201
|
+
"anchorChain",
|
|
1202
|
+
[v("x"), v("c")],
|
|
1203
|
+
[lit("anchorChain", v("x"), v("r")), lit("call", v("r"), v("c"))]
|
|
1204
|
+
),
|
|
1205
|
+
rule(
|
|
1206
|
+
"anchorChain",
|
|
1207
|
+
[v("x"), v("o")],
|
|
1208
|
+
[
|
|
1209
|
+
lit("anchorChain", v("x"), v("y")),
|
|
1210
|
+
lit("readsProperty", v("y"), v("o"), v("n"))
|
|
1211
|
+
]
|
|
1212
|
+
),
|
|
1213
|
+
rule(
|
|
1214
|
+
"wantedAnchorCall",
|
|
1215
|
+
[v("x"), v("r")],
|
|
1216
|
+
[lit("anchorChain", v("x"), v("r")), lit("call", v("r"), v("c"))]
|
|
915
1217
|
)
|
|
916
1218
|
];
|
|
917
1219
|
var ANSWER_RELATIONS = [
|
|
@@ -920,11 +1222,16 @@ var ANSWER_RELATIONS = [
|
|
|
920
1222
|
export {
|
|
921
1223
|
ANSWER_RELATIONS,
|
|
922
1224
|
FACT_CONTRACT_CASES,
|
|
1225
|
+
NAMESPACE_IMPORT,
|
|
1226
|
+
NAMESPACE_IMPORT_NAME,
|
|
923
1227
|
RESOLUTION_QUESTIONS,
|
|
924
1228
|
RESOLUTION_RULES,
|
|
925
1229
|
RESULT_STEP,
|
|
926
1230
|
VALUE_STEP,
|
|
1231
|
+
agreedMountPrefix,
|
|
927
1232
|
checkFactContract,
|
|
928
1233
|
explainResolutionProof,
|
|
1234
|
+
joinMountedPath,
|
|
1235
|
+
mountPathsOf,
|
|
929
1236
|
renderExplanation
|
|
930
1237
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/resolution",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
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.
|
|
56
|
+
"@suss/datalog": "0.21.0"
|
|
57
57
|
}
|
|
58
58
|
}
|